Task.Start,Task.RunSynchronously和同步上下文

avo*_*avo 8 .net c# task synchronizationcontext task-parallel-library

我手动构建一个任务:

var task = new Task(() => 
    Debug.WriteLine("Task"));
Run Code Online (Sandbox Code Playgroud)

然后手动启动它:

task.Start(TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)

我希望它能通过预定SynchronizationContext.Post.

但如果以这种方式启动它:

task.RunSynchronously(TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)

是通过SynchronizationContext.Send调用任务的lambda 来执行还是直接执行?

nos*_*tio 9

它会通过SynchronizationContext.Send执行,还是直接通过调用任务的lambda来执行?

这是正在发生的事情.首先,Task.RunSynchronously尝试通过调用来执行任务scheduler.TryExecuteTaskInline.如果是SynchronizationContextTaskScheduler,就是这样:

protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
    return ((SynchronizationContext.Current == this.m_synchronizationContext) && base.TryExecuteTask(task));
}
Run Code Online (Sandbox Code Playgroud)

因此,如果在同一个同步上下文中,任务lambda将直接在内部执行base.TryExecuteTask.

否则,Task.RunSynchronously使用任务调度程序对任务进行排队,并WaitHandle使用阻塞等待阻止任务.

SynchronizationContext.Send没有涉足任何情况.

有趣的是这个.AFAIK,在WPF DispatcherSynchronizationContext中,主UI线程上可能有多个上下文,每个顶级窗口一个上下文.因此,理论上RunSynchronously可能会导致死锁.我要验证这一点.

更新后,WPF中的死锁是真的.这是如何重现:

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

        this.Loaded += MainWindow_Loaded;
    }

    void MainWindow_Loaded(object sMainWindow, RoutedEventArgs eMainWindow)
    {
        var task = new Task(() =>
            Debug.WriteLine("Task"));

        var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

        var window1 = new Window();
        window1.Loaded += (sWindow1, eWindow1) =>
        {
            // Deadlock in WPF
            task.RunSynchronously(scheduler);
            Debug.WriteLine("Done!");
        };
        window1.Show();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Thomas,没有竞争,但是任务完成回调是通过 SynchronizationContext.Post 发布的,然后线程在 task.RunSynchronously 内阻塞在同一任务的句柄上,从而导致死锁。我将通过电话进行此操作,无法详细说明,但您可以搜索 Stephen Clearly 的“不要阻止异步代码”。 (2认同)