Вопрос

I construct a task manually:

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

Then start it manually:

task.Start(TaskScheduler.FromCurrentSynchronizationContext());

I expect it to be be scheduled via SynchronizationContext.Post.

But if start it this way:

task.RunSynchronously(TaskScheduler.FromCurrentSynchronizationContext());

Will it be executed via SynchronizationContext.Send, or directly by calling the task's lambda?

Это было полезно?

Решение

Will it be executed via SynchronizationContext.Send, or directly by calling the task's lambda?

Here's what's going on. First, Task.RunSynchronously tries to execute the task by calling scheduler.TryExecuteTaskInline. In case with SynchronizationContextTaskScheduler, it's this:

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

So, if on the same synchronization context, the task lambda will be executed directly inside base.TryExecuteTask.

Otherwise, Task.RunSynchronously queues the task with the task scheduler, and blocks on the task's WaitHandle with the blocking wait.

SynchronizationContext.Send does not get involved in any case.

What's interesting about this. AFAIK, in WPF there may be several DispatcherSynchronizationContext contexts on the main UI thread, one per top-level window. Thus, in theory, RunSynchronously may result in a deadlock. I'm going to verify this.

Updated, the deadlock in WPF is real. Here is how to repro:

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();
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top