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();
}
}