C# 在新任务中创建WinForm UI控件后等待是否会导致死锁?

C# 在新任务中创建WinForm UI控件后等待是否会导致死锁?,c#,winforms,async-await,C#,Winforms,Async Await,我在WinForms应用程序中有这种奇怪的行为。我从主窗体的窗体事件处理程序中调用此任务 Task.Run(() => WatchHistory(CancellationTokenSource.Token)); 函数定义如下。FinishedRequestItem对象是WinForms UI控件。它在等待任务时死锁。延迟。我注意到一个新的同步上下文是在FinishedRequestItem对象创建之后创建的。这个上下文与主表单UI的上下文不同 private async void

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

Task.Run(() => WatchHistory(CancellationTokenSource.Token));
函数定义如下。FinishedRequestItem对象是WinForms UI控件。它在等待任务时死锁。延迟。我注意到一个新的同步上下文是在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);
        }

    }
private async void WatchHistory(CancellationToken CancellationToken)
{
而(!cancellationToken.IsCancellationRequested)
{
var completedRequests=等待processContext.ProcessRequests.ToListSync(cancellationToken);
if(completedRequests.Any())
{
FinishedRequestItem entry=Program.Container.GetInstance();
}
等待任务延迟(CurrentHistoryRefresh毫秒,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);
        }
while(!cancellationToken.IsCancellationRequested)
{
var completedRequests=等待processContext.ProcessRequests.ToListSync(cancellationToken);
if(completedRequests.Any())
{
FinishedRequestItem条目=空;
调用((操作)委托()
{
entry=Program.Container.GetInstance();
});
}
等待任务延迟(CurrentHistoryRefresh毫秒,cancellationToken);
}

首先,您应该在每个不希望
等待
在当前同步上下文上恢复的位置使用
任务。ConfigureAwait(false)
。如果您是为
任务.Delay
调用这样做的,那么您就不会有这个问题(但很可能是另一个问题)

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

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

Program.Container.GetInstance<FinishedRequestItem>()  
Program.Container.GetInstance()
当前同步上下文已更改,然后
Task.Delay
尝试在该上下文上恢复,但当然不能,因为该线程上没有运行消息泵(这是
WindowsFormsSynchronizationContext
正常工作所必需的)


一般来说,您不应该在非UI线程上创建UI元素。

您应该只在UI线程上创建UI控件。您也不应该使用
async void
,而是使用
async Task
。我在更新的代码中使用async Task,我知道这是一种更好的模式。但问题依然存在。我认为这与任务有关。延迟尝试在等待它的同一线程中恢复。