Task.Start, Task.RunSynchronously и контекст синхронизации

Я строю задачу вручную:

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

Затем запустите его вручную:

task.Start(TaskScheduler.FromCurrentSynchronizationContext());

Я ожидаю, что это будет запланировано через SynchronizationContext.Post,

Но если начать так:

task.RunSynchronously(TaskScheduler.FromCurrentSynchronizationContext());

Будет ли это выполнено через SynchronizationContext.Sendили напрямую, вызывая лямбду задачи?

1 ответ

Решение

Будет ли он выполняться через SynchronizationContext.Send или напрямую, вызывая лямбду задачи?

Вот что происходит. Первый, Task.RunSynchronously пытается выполнить задачу, вызвав scheduler.TryExecuteTaskInline, В случае с SynchronizationContextTaskScheduler Вот так:

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

Таким образом, если в том же контексте синхронизации, лямбда-задача будет выполняться непосредственно внутри base.TryExecuteTask,

Иначе, Task.RunSynchronously ставит задачу в очередь с помощью планировщика задач и блокирует задачи WaitHandle с блокировочным ожиданием.

SynchronizationContext.Send не ввязывается ни в коем случае.

Что интересного в этом. AFAIK, в WPF может быть несколько 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();
    }
}
Другие вопросы по тегам