Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Task.Start, Task.RunSynchronously and synchronization context

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?

like image 273
avo Avatar asked Feb 20 '14 13:02

avo


People also ask

Is Task run async or sync?

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.

Does Task run run synchronously?

Even though the task runs synchronously, the calling thread should still call Wait to handle any exceptions that the task might throw.

What is the difference between Task run and Task factory StartNew?

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!

What is difference between Task and thread in C#?

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.


1 Answers

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();
    }
}
like image 65
noseratio Avatar answered Oct 01 '22 02:10

noseratio