Reputation: 233
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
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
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