Answered by:
Constructor invoke async method

Question
-
Hi all,
Sometime, I want in the class constructor call a async method:
public class MyClass { public MyClass() {
ShowAsync().Wait(); Console.WriteLine("end"); } async Task<string> ShowAsync() { var str= await SomethingAsync();
Console.WriteLine(str); return str; } }
when I new MyClass() , it will stop in await SomethingAsync(),
This issue will be happened in metro, but in the desktop, it will work correct.
This is a bug in metro?
How can I invoke the method in constructor?
Thanks! Damon.Tian
- Edited by Damon.Tian Monday, April 9, 2012 5:27 AM
Monday, April 9, 2012 5:17 AM
Answers
-
They're different because in a console app, SynchronizationContext.Current will return null by default, and in a Metro style app, SynchronizationContext.Current will return a context for the UI thread when invoked from the UI. Thus by default "await" when invoked from the UI thread of a Metro style app will force continuations back to the UI thread. This is what creates the aforementioned deadlock. See http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx for more details on the phenomenon.
- Proposed as answer by Stephen Toub - MSFTMicrosoft employee Tuesday, April 10, 2012 4:39 PM
- Marked as answer by Damon.Tian Friday, April 13, 2012 9:30 AM
Tuesday, April 10, 2012 4:39 PM -
Damon,
This definitely creates deadlock as explained above.
Constructors cannot be declared with async because they're not good fit conceptually.
Also I strongly advise you to reconsider your design here.
Lastly you might want to have factory method such as:
public static async MyClass GetMyClass() { MyClass myObj = new MyClass(); await myObj.ShowAsync();
return myObj; }
Please be aware that calling Task.Wait is not the same as awaiting.
- ngm
- Edited by ngmO11 Monday, April 9, 2012 7:41 PM
- Marked as answer by Damon.Tian Friday, April 13, 2012 9:31 AM
Monday, April 9, 2012 7:40 PM
All replies
-
Damon,
It looks to me like perfect deadlock i.e. the thread entered MyClass constructor is blocking by waiting for ShowAsync method to finish, but ShowAsync is trying to return back on that blocking thread.
It has nothing to do with constructor itself. I seriously doubt this works on "desktop".
Probably it can only work if current SynchronizationContext is set to null which is not the case by default in WinRT or WPF. The reason being that after SomethingAsync finished it will resume on different thread.
Hope it helps,
- ngm
Monday, April 9, 2012 5:57 AM -
Hi ngmO11,
I'm sure it can work on desktop with using C#5.0.
I want to invoke the mothed in constructor, but consotructor can't be add the async ,it doesn't have retrun.
So I only want wait the mothed end.
If the mothed has return,I can use Task.result to wait the end, but sometime it will happed the issue.
when I debug it , if the method can be debug, it will work correct. then without debug,it will dead.
And if the method will be invoke long time, it's no response.
So I think this is a bug in metro.
Thanks! Damon.Tian
Monday, April 9, 2012 11:56 AM -
Damon,
This definitely creates deadlock as explained above.
Constructors cannot be declared with async because they're not good fit conceptually.
Also I strongly advise you to reconsider your design here.
Lastly you might want to have factory method such as:
public static async MyClass GetMyClass() { MyClass myObj = new MyClass(); await myObj.ShowAsync();
return myObj; }
Please be aware that calling Task.Wait is not the same as awaiting.
- ngm
- Edited by ngmO11 Monday, April 9, 2012 7:41 PM
- Marked as answer by Damon.Tian Friday, April 13, 2012 9:31 AM
Monday, April 9, 2012 7:40 PM -
Hi ngmO11,
Thanks for your following.
I know how to bypass this issue, But I think it's a bug. above code can work on desktop ,not work in metro.
I think them are same.
I need somebody tell me why they are different.
Thanks! Damon.Tian
Tuesday, April 10, 2012 2:55 AM -
They're different because in a console app, SynchronizationContext.Current will return null by default, and in a Metro style app, SynchronizationContext.Current will return a context for the UI thread when invoked from the UI. Thus by default "await" when invoked from the UI thread of a Metro style app will force continuations back to the UI thread. This is what creates the aforementioned deadlock. See http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx for more details on the phenomenon.
- Proposed as answer by Stephen Toub - MSFTMicrosoft employee Tuesday, April 10, 2012 4:39 PM
- Marked as answer by Damon.Tian Friday, April 13, 2012 9:30 AM
Tuesday, April 10, 2012 4:39 PM -
Steve's reply to the question is right.
Are you certain that your deadlock occurs here
var str = await SomethingAsync();
and not here
ShowAsync().Wait();
?
You should generally never do a blocking wait on a Task from a UI thread.
It is very common that a Task needs to interact with the UI thread in order to complete properly, and if the UI thread is blocked it cannot participate in the interaction and your will deadlock. This is why you observe this problem in a UI application and not in a console application. This includes APIs like Task.Wait(), Task<T>.Result and other APIs that block waiting for a Task completion.To avoid this you can ensure that your blocking wait is performed on the thread pool by wrapping your code into a delegate passed to Task.Run(..), or by avoiding the blocking wait altogether and using await instead. Sure, you cannot await in a constructor. You could work around my using a static factory method which can be marked async so you can await in it.
- Proposed as answer by macrogreg Wednesday, April 11, 2012 9:03 PM
- Unproposed as answer by Damon.Tian Friday, April 13, 2012 9:32 AM
Wednesday, April 11, 2012 9:03 PM -
As macrogreg mentioned, you can wrap the code inside Task.Run. For example, in my view model's constructor I need to prepopulate a WPF ComboBox from a database table and want to do that asynchronously. So inside the constructor this works:
_cts = new CancellationTokenSource(); // Allows us to run an async Task from the non-async constructor Task.Run(async () => await SelectInputTablesFromDb(InputDbInfo, _cts.Token));
The Task signature looks like this:
public async Task SelectInputTablesFromDb(DBConnectionManager.DbInfo dbInfo, CancellationToken cancellationToken) { // ...do work }
stonetip
Friday, June 21, 2013 3:43 PM