By ValidfroM


2018-10-11 12:02:22 8 Comments

I just wonder what's the method for? In what kind of scenario I can use this method.

My initial thought is RunSynchronously is for calling an async method and running that synchronously without causing a deadlock issue like what .wait() does.

However, according to MSDN,

Ordinarily, tasks are executed asynchronously on a thread pool thread and do not block the calling thread. Tasks executed by calling the RunSynchronously() method are associated with the current TaskScheduler and are run on the calling thread. If the target scheduler does not support running this task on the calling thread, the task will be scheduled for execution on the schedule, and the calling thread will block until the task has completed execution

Why need a TaskScheduler here, if the task going to run on the calling thread?

2 comments

@Fabjan 2018-10-11 13:07:51

First let's have a look into this code:

public async static Task<int> MyAsyncMethod()
{
   await Task.Delay(100);
   return 100;
}

//Task.Delay(5000).RunSynchronously();                        // bang
//Task.Run(() => Thread.Sleep(5000)).RunSynchronously();     // bang
// MyAsyncMethod().RunSynchronously();                      // bang

var t = new Task(() => Thread.Sleep(5000));
t.RunSynchronously();                                     // works

In this example we've tried to invoke RunSynchronously on task that:

  • Returns other Task with result (promise task)
  • Is a 'hot' task
  • Another promise task created by async await
  • 'Cold' task with delegate

What statuses will they have after creation ?

  • Waiting for activation
  • WaitingToRun
  • WaitingForActivation
  • Created

All 'hot' tasks are created with status WaitingForActivationor WaitingToRun and are associated with a task scheduler.

Method RunSynchronously only knows how to work with 'cold' tasks that contain delegate and that have status of Created.

Conclusion:

The method RunSynchronously had probably appeared when there were no 'hot' tasks or they hadn't been extensively used and was created for a specific purpose.

We might want to use it in case when we need a 'cold' task with custom TaskScheduler, otherwise it t is outdated and useless.

For running a 'hot' task synchronously (which we should avoid) we could use task.GetAwaiter().GetResult(). As a bonus it will return original exception and not an instance of AggregateException.

@Liam 2018-10-11 13:27:22

It's probably worth noting that Thread has (basically) been superseded with Task (Task.Delay()) so Thread.Sleep could be considered legacy too now. Which is why it works with the legacy RunSynchronously

@Stefano d'Antonio 2018-10-11 12:34:39

RunSynchronously delegates the decision of when to start the task to the current task scheduler (or the one passed as argument).

I am not sure why it is there (maybe for internal or legacy use), but it is hard to think of a useful use case in the current versions of .NET. @Fabjan has a possible explanation in his comment to the question.

RunSynchronously asks the scheduler to run it synchronously but then the scheduler could very well ignore the hint and run it in a thread pool thread and your current thread will synchronously block until it is completed.

The scheduler does not have to run it on the current thread and does not have to run it immediately although I think it is what will happen on common schedulers (ThreadPoolTaskScheduler and common UI schedulers).

RunSynchronously will also throw an exception if the task has already been started or is completed/faulted (this means you will not be able to use it on async methods).

This code may clarify the different behaviour:

Wait and Result don't run the task at all, they just wait for the task completion on the current thread and block it until the completion so if we want to compare, we can compare Start and Wait to RunSynchronously:

class Scheduler : TaskScheduler
{
    protected override void QueueTask(Task task) => 
        Console.WriteLine("QueueTask");

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        Console.WriteLine("TryExecuteTaskInline");

        return false;
    }

    protected override IEnumerable<Task> GetScheduledTasks() => throw new NotImplementedException();
}

static class Program
{
    static void Main()
    {
        var taskToStart = new Task(() => { });
        var taskToRunSynchronously = new Task(() => { });

        taskToStart.Start(new Scheduler());
        taskToRunSynchronously.RunSynchronously(new Scheduler());
    }
}

If you try and comment Start or RunSynchronously and run the code, you will see that Start tries and queue the task to the scheduler while RunSynchronously will try and execute it inline and if failing (return false), it will just queue it.

Related Questions

Sponsored Content

57 Answered Questions

[SOLVED] What is the difference between String and string in C#?

12 Answered Questions

[SOLVED] Asynchronously wait for Task<T> to complete with timeout

33 Answered Questions

[SOLVED] What is the difference between a process and a thread?

20 Answered Questions

9 Answered Questions

[SOLVED] What are the correct version numbers for C#?

11 Answered Questions

[SOLVED] Asynchronous vs Multithreading - Is there a difference?

25 Answered Questions

[SOLVED] What is the best way to iterate over a dictionary?

  • 2008-09-26 18:20:06
  • Jake Stewart
  • 1244483 View
  • 2087 Score
  • 25 Answer
  • Tags:   c# dictionary loops

4 Answered Questions

2 Answered Questions

Sponsored Content