【问题标题】:Async wait block Main UI异步等待块主 UI
【发布时间】:2013-10-12 22:28:10
【问题描述】:

我正在使用新的异步等待功能从 C# 中的后台工作程序升级。在下面的代码中,我尝试使用 ContinueWith 方法复制多个任务的执行。

        Task t1 = new Task
        (
            () =>
            {
                Thread.Sleep(10000);

                // make the Task throw an exception
                MessageBox.Show("This is T1");
            }
        );

        Task t2 = t1.ContinueWith
        (
            (predecessorTask) =>
            {

                if (predecessorTask.Exception != null)
                {
                    MessageBox.Show("Predecessor Exception within Continuation");

                    return;
                }

                Thread.Sleep(1000);
                MessageBox.Show("This is Continuation");
            },

            TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion
        );

        t1.Start();

        try
        {
           t1.Wait(); <------ Comment 
           t2.Wait(); <------ Comment 
        }
        catch (AggregateException ex)
        {
            MessageBox.Show(ex.InnerException.Message);
        }  

我的问题是当我评论 t1.wait 和 t2.wait 任务没有阻塞 UI。但是,当我取消注释 t1.wait 和 t2.wait UI 阻塞,直到线程完成。期望的行为是在不阻塞 UI 的情况下捕获 try/catch 块中的错误。我错过了什么?

【问题讨论】:

  • 它阻塞是因为你告诉它...
  • 我怎么能不阻塞用户界面,仍然在 try/catch 块中等待。
  • 您希望如何在这里抛出异常?我看不出会发生在哪里..
  • “我正在使用新的异步等待功能”在哪里?我在您的代码中的任何地方都没有看到它们。

标签: c# multithreading asynchronous async-await c#-5.0


【解决方案1】:

当您使用Task.Wait() 时,您基本上是在说“在这里等待我的任务完成”。这就是你阻塞线程的原因。在任务中处理异常的一个好方法是使用Task.ContinueWith 重载并将OnlyOnFaulted 作为TaskContinuationOption 传递,如下所示:

Task yourTask = new Task {...};
yourTask.ContinueWith( t=> { /*handle expected exceptions*/ }, TaskContinuationOptions.OnlyOnFaulted );

【讨论】:

    【解决方案2】:

    如果它在 UI 事件处理程序中运行,您可以将 async 修饰符添加到方法签名并将 t1.Wait() 更改为 await t1。这会将控制权返回给 UI 线程,并且当 Thread.Sleep 完成时,将执行延续并捕获任何异常。

    【讨论】:

      【解决方案3】:

      如果您要使用Task-based Asynchronous Pattern,那么您应该使用推荐的指南。我有一个 MSDN article 描述了其中的许多。

      特别是:

      • 使用Task.Run 而不是Task 构造函数和Task.Start
      • 使用await 而不是ContinueWith
      • 请勿使用AttachedToParent

      如果您应用这些更改,您的代码将如下所示:

      try
      {
        await Task.Run(() =>
        {
          Thread.Sleep(10000);
      
          // make the Task throw an exception
          MessageBox.Show("This is T1");
        });
        await Task.Run(() =>
        {
          Thread.Sleep(1000);
          MessageBox.Show("This is Continuation");
        });
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }  
      

      【讨论】:

      • 其实我已经阅读了您的文章和您的博客,然后才发布此内容。非常好的东西,但对于像我这样的新手来说,在等待/异步中有点先进。因此,根据您的解决方案,我可以通过将两个 Task.Run 放在一行中来复制 ContinueWith 的行为,而忘记创建任务和 Task.start。如果是,那么当您的解决方案看起来非常优雅时。
      • Task.Run 替换了Task 构造函数和Task.Startawait 替换 ContinueWith。我有一篇 intro to async 博文,您可能会觉得有帮助。
      • 一定会去看看的。谢谢
      猜你喜欢
      • 1970-01-01
      • 2016-03-13
      • 1970-01-01
      • 2015-01-09
      • 1970-01-01
      • 2019-03-04
      • 1970-01-01
      • 2023-03-12
      • 2016-07-07
      相关资源
      最近更新 更多