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