Pregunta

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?

¿Fue útil?

Solución

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();
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top