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

时间:2014-02-20 13:37:06

标签: c# .net task-parallel-library task synchronizationcontext

我手动构建一个任务:

var task = new Task(() => 
    Debug.WriteLine("Task"));

然后手动启动:

task.Start(TaskScheduler.FromCurrentSynchronizationContext());

我希望通过SynchronizationContext.Post安排好。

但如果以这种方式开始:

task.RunSynchronously(TaskScheduler.FromCurrentSynchronizationContext());

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

1 个答案:

答案 0 :(得分: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));
}

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

否则,Task.RunSynchronously将任务与任务调度程序排队,并使用阻塞等待阻塞任务的WaitHandle

SynchronizationContext.Send 在任何情况下都会参与其中。

这有趣的是什么。 AFAIK,在WPF中,主UI线程上可能有几个DispatcherSynchronizationContext上下文,每个顶级窗口一个。因此,理论上,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();
    }
}