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?
NET, Task. Run is used to asynchronously execute CPU-bound code. Let's say there is a method which does some CPU-bound work. Example : looping through a large array and doing some complex computation on each element of the array.
Even though the task runs synchronously, the calling thread should still call Wait to handle any exceptions that the task might throw.
Task. Run(action) internally uses the default TaskScheduler , which means it always offloads a task to the thread pool. StartNew(action) , on the other hand, uses the scheduler of the current thread which may not use thread pool at all!
Differences Between Task And ThreadThe Thread class is used for creating and manipulating a thread in Windows. A Task represents some asynchronous operation and is part of the Task Parallel Library, a set of APIs for running tasks asynchronously and in parallel. The task can return a result.
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With