Derek Patton
Derek Patton

Reputation: 233

When should Task.Run() be used?

What's the difference between these two approaches:

public static int Main(string[] args)
{
    string result;  

    Task.Run(async () =>
    {
        Task<string> getStringTask = GetStringAsync();
        result = await validationsTask;
    }).Wait();

    Console.WriteLine(result);
}

and

public static int Main(string[] args)
{
    Task<string> getStringTask = GetStringAsync();
    getStringTask.Wait();
    string result = getStringTask.Result;

    Console.WriteLine(result);
}

I've seen a lot of people using the first approach and I'm not sure why. Is there any particular advantage? Which one is recommended for waiting async methods inside main of a Console Application?

Upvotes: 3

Views: 250

Answers (2)

NeddySpaghetti
NeddySpaghetti

Reputation: 13495

Is there any particular advantage?

Usually with async methods the operation is initialized synchronously and then the wait can be asynchronous with await or syncrhnous with Wait(). The Main method can't be async so you are force to block with Wait() there or you can do a Console.ReadKey() to run until the user presses a key.

Task.Run(async () => ... ) can be quite useful when the async operation is expensive to initialize. That way you allow the main thread to continue while the operation is initializing.

Which one is recommended for waiting async methods inside main of a Console Application?

I would use a slightly modified version of the second approach. You can add a MainAsync method and call that from Main then you can use await inside it.

public static async Task MainAsync()
{
    string result = await GetStringAsync();    
    Console.WriteLine(result);
}

public static int Main(string[] args)
{
    MainAsync().Wait();
}

Also with console apps there is no risk of deadlock as there is no SynchronizationContext and the default thread pool one gets used.

Upvotes: 2

Khanh TO
Khanh TO

Reputation: 48992

The first approach continues execution after the asynch function is finished using a thread pool thread while the second approach continues execution using the calling thread that starts the asynch function.

With the second approach, there is a possibility of deadlocks. For example (similar to an example extracted from the book CLR via C#):

    public static int Main(string[] args)
    {
        Task<string> getStringTask = GetStringAsync();
        string result = getStringTask.Result; //the main thread is blocked waiting.

        Console.WriteLine(result);
    }

    public Task<string> GetStringAsync()
    {  
        // Issue the HTTP request and let the thread return from GetHttp 
        HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/"); 
        // We never get here: The main thread is waiting for this method to finish but this method 
        // can't finish because the main thread is waiting for it to finish --> DEADLOCK! 
        return await msg.Content.ReadAsStringAsync();
    }

So the first approach avoids this problem:

public static int Main(string[] args)
{
    string result;  

    Task.Run(async () =>
    {
        // We run on a thread pool thread
        Task<string> getStringTask = GetStringAsync();
        // We do get here because any thread pool thread can execute this code, we don't need the main thread.
        result = await validationsTask;
    }).Wait();

    Console.WriteLine(result);
}

Another solution is using ConfigureAwait(false), extracted from the book:

Passing true to this method gives you the same behavior as not calling the method at all. But, if you pass false, the await operator does not query the calling thread’s SynchronizationContext object and, when a thread pool thread completes theTask, it simply completes it and the code after the await operator executes via the thread pool thread.

    public Task<string> GetStringAsync()
    {  
         HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/").ConfigureAwait(false); 
         // We DO get here now because a thread pool can execute this code 
// as opposed to forcing the main thread to execute it.   
         return await msg.Content.ReadAsStringAsync().ConfigureAwait(false);
    }

Upvotes: 0

Related Questions