等待在新任务中创建WinForm UI控件导致死锁?

时间:2015-11-09 19:00:00

标签: c# winforms async-await

我在WinForms应用程序中有这种奇怪的行为。我从我的主窗体的Form_Shown事件处理程序中调用此任务。

Task.Run(() => WatchHistory(CancellationTokenSource.Token));

功能定义如下。 FinishedRequestItem对象是一个WinForms UI控件。它死了等待Task.Delay。我注意到,一旦创建了FinishedRequestItem对象,就会创建一个新的同步上下文。这个上下文与主窗体UI的上下文不同。

    private async void WatchHistory(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var completedRequests = await processContext.ProcessRequests.ToListAsync(cancellationToken);
            if (completedRequests.Any())
            {
                FinishedRequestItem entry = Program.Container.GetInstance<FinishedRequestItem>();

            }

            await Task.Delay(CurrentHistoryRefreshMilliseconds, cancellationToken);
        }

    }

但是,如果我使用Invoke方法创建UI控件,如下所示,它不会死锁。我很好奇为什么?由于任务没有在UI上下文中运行,是什么阻止Task.Delay在上面的代码中恢复?

while (!cancellationToken.IsCancellationRequested)
        {
            var completedRequests = await processContext.ProcessRequests.ToListAsync(cancellationToken);
            if (completedRequests.Any())
            {

                FinishedRequestItem entry = null;

                this.Invoke((Action)delegate()
                {
                    entry = Program.Container.GetInstance<FinishedRequestItem>();
                });

            }

            await Task.Delay(CurrentHistoryRefreshMilliseconds, cancellationToken);
        }

1 个答案:

答案 0 :(得分:2)

首先,您应该在每个不希望Task.ConfigureAwait(false)在当前同步上下文中恢复的地方使用await。如果您为Task.Delay电话这样做,则不会出现此问题(但很可能是另一个问题)。

  

但是,如果我使用Invoke方法创建UI控件,如下所示,它不会死锁。我很好奇为什么?由于任务没有在UI上下文中运行,是什么阻止Task.Delay在上面的代码中恢复?

实际上,您使用它的方式,Task.Delay将尝试在您调用它时的当前同步上下文中恢复。这就是重点。通常,线程池线程没有同步上下文(或使用有效不执行任何同步的默认上下文)。但默认情况下,每个System.Windows.Forms.Control都会在其构造函数中安装(设置SynchronizationContext.CurrentWindowsFormsSynchronizationContext。因此在通话后

Program.Container.GetInstance<FinishedRequestItem>()  

当前同步上下文已更改,然后Task.Delay尝试在该上下文中继续,但当然不能,因为该线程上没有运行消息泵(这是{{1}所必需的正常运作)。

通常,您不应在非UI线程上创建UI元素。