【问题标题】:Task's continuation (built by async/await) is running on main thread in a WPF application, but on child in a console application任务的延续(由 async/await 构建)在 WPF 应用程序的主线程上运行,但在控制台应用程序的子线程上运行
【发布时间】:2014-07-11 05:07:45
【问题描述】:

假设我有一个简单的C# 控制台应用程序:

class Program
{
    static async void func()
    {
        Thread.CurrentThread.Name = "main";
        await Task.Run(() =>
        {
            Thread.CurrentThread.Name = "child";
            Thread.Sleep(5000);
        });
        Console.WriteLine("continuation is running on {0} thread", Thread.CurrentThread.Name);
    }

    static void Main(string[] args)
    {
        func();
        Thread.Sleep(10000);
    }
}

当 5000 毫秒过去时,我们会看到“继续在子线程上运行”消息。当另一个 5000 毫秒过去时,主线程完成其工作并关闭应用程序。它看起来很合乎逻辑:异步任务及其延续运行在同一个子线程上。

但假设现在我有一个简单的WPF 应用程序:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    async private void mainWnd_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Thread.CurrentThread.Name = "main";
        await Task.Run(() =>
        {
            Thread.CurrentThread.Name = "child";
            Thread.Sleep(5000);
        });
        this.Title = string.Format("continuation is running on {0} thread", Thread.CurrentThread.Name);
    }

    private void mainWnd_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        Thread.Sleep(10000);
    }
}

现在当我们按下鼠标左键并经过 5000 毫秒时,我们会看到“continuation is running on main thread”标题。此外,如果我们按左键然后右键,应用程序将首先运行mainWnd_MouseLeftButtonDown handler,然后mainWnd_MouseRightButtonDown handler(在主线程上),主线程将休眠10000 ms,然后从mainWnd_MouseLeftButtonDown继续异步任务仍将在主线程上执行。

为什么async-await 机制在这两种情况下会有所不同?

我知道WPF 中的方法可以通过Dispatcher.Invoke 在UI 线程上显式运行,但async-await 机制不是WPF 特有的,因此它的行为在任何类型的应用程序中应该是相同的,应该不是吗?

任何帮助将不胜感激。

【问题讨论】:

    标签: c# wpf asynchronous task async-await


    【解决方案1】:

    async-await 尊重当前作用域的SynchronizationContext。这意味着在异步操作开始时捕获上下文(如果存在),并在其结束时在捕获的上下文中安排延续。

    UI 应用程序 (WPF/Winforms) 使用 SynchronizationContext,它只允许主 (UI) 线程与 UI 元素交互,因此它可以与 async-await 无缝协作.

    ASP.Net 也有自己的SynchronizationContext,称为AspNetSynchronizationContext(令人惊讶)。所以不一定是UISingle Thread Apartments


    如果您想禁用有用的SynchronizationContext 捕获,您只需要使用ConfigureAwait

    await Task.Run(() =>
    {
        Thread.CurrentThread.Name = "child";
        Thread.Sleep(5000);
    }).ConfigureAwait(false);
    

    有关 SynchronizationContexts 的更多信息:It's All About the SynchronizationContext

    【讨论】:

      猜你喜欢
      • 2013-05-30
      • 1970-01-01
      • 1970-01-01
      • 2013-01-18
      • 2016-06-11
      • 2021-10-25
      • 1970-01-01
      • 2011-04-09
      • 1970-01-01
      相关资源
      最近更新 更多