8

我手动构建一个任务:

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

然后手动启动它:

task.Start(TaskScheduler.FromCurrentSynchronizationContext());

我希望它通过SynchronizationContext.Post.

但如果以这种方式开始:

task.RunSynchronously(TaskScheduler.FromCurrentSynchronizationContext());

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

4

1 回答 1

10

它会通过 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();
    }
}
于 2014-02-20T20:58:11.540 回答