【问题标题】:Is it safe to change ui properties after await in async methods?在异步方法中等待之后更改 ui 属性是否安全?
【发布时间】:2015-12-06 08:50:49
【问题描述】:

我在编写以下程序时搞砸了 c# 的 await/async:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        Test();

        while (true) { }
    }

    static async void Test()
    {
        var t = Task.Run(()=> Thread.Sleep(1000));

        await t;

        throw new Exception("This exception happens in a worker thread");
    }
}

当我运行这个程序时,我可以在 Visual Studio 的线程窗口中清楚地看到异常发生在 Worker Thread,而不是 Main Thread,这让我相信当我等待任务时,我的方法可以由另一个线程完成。

但是,当我打开一个新的 Windows 窗体应用程序并添加以下事件处理程序时:

    async void button1_Click(object sender, EventArgs e)
    {
        var task = Task.Run(() => Thread.Sleep(1000));

        await task;

        button1.Text = "I'm in the main thread!"; //this works
    }

这可行,所以我注意到在控制台应用程序中,我的 Test 函数由工作线程恢复,而在 Windows 窗体应用程序中,它总是由主线程恢复。

这是为什么?靠谱吗?

【问题讨论】:

  • 当您等待任务时,任务中抛出的任何异常都会在等待恢复的上下文中重新抛出(即窗口程序中的 UI 线程)。这是可靠的。
  • 是的,我也在某处读过,但是在控制台应用程序中,如果我在 throw new Exception 语句中设置断点并检查线程窗口,我会看到一个工作线程作为当前线程

标签: c# asynchronous async-await


【解决方案1】:

是的。很靠谱。

当您执行await 时,当前的SynchronizationContext 被捕获,这样当它在await 之后继续时,此上下文将用于执行代码。

在 GUI 应用程序中,当前的SynchronizationContext 是一个将在 UI 线程上执行代码的上下文。

在控制台应用程序中,当前的SynchronizationContextnull,因此任务在线程池线程上继续。

看看this blog post

【讨论】:

    【解决方案2】:

    正如我在async intro 中解释的那样,默认情况下await 将捕获“上下文”并在该上下文中恢复其async 方法的执行。

    从技术上讲,这个上下文是SynchronizationContext.Current 除非它是null,在这种情况下它是TaskScheduler.Current

    在日常术语中,这意味着如果方法在 UI 线程上运行,则上下文是 UI 上下文;如果该方法为 ASP.NET 请求提供服务,则它是一个 ASP.NET 请求上下文;它很可能是所有其他情况下的线程池上下文。

    请注意,这是 默认 行为。您可以通过等待ConfigureAwait(continueOnCapturedContext: false) 的结果来指定该方法不需要在其上下文中恢复。在这种情况下,方法的其余部分很可能会在线程池线程上执行;但从技术上讲,这只是意味着该方法将在“某处”执行,而您不在乎在哪里。

    【讨论】:

      【解决方案3】:

      是的,这是正确的,也是 async / await 的预期用途之一。 您正在等待的Task 是异步发生的。当它恢复时,它会在调用线程上恢复,在 WinForms 事件处理程序的情况下它将是 UI 线程。

      请注意,您可以使用Task.ConfigureAwait(False) 更改此行为。以这种方式配置任务时,它不会将控制权交还给原始线程,而是会在任务的线程上恢复。

      所有这些行为都取决于应用程序的当前SynchonizationContext。某些应用程序类型将具有不同的上下文,可以巧妙地改变行为。控制台应用程序就是一个很好的例子,因为它们默认不设置同步上下文,并将在任务的线程上恢复。如果需要,您可以通过为控制台应用程序创建上下文来更改此设置,如 here 所示。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-15
        • 2018-12-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多