Reputation: 37633
When I do this it doesn't return Task<TokenModel>
I mean this line is freezing tokenModel = await response.Content.ReadAsAsync<TokenModel>();
Foo()
{
var r = IsLoginOk();
}
public async Task<TokenModel> IsLoginOk()
{
var handler = new HttpClientHandler();
handler.UseDefaultCredentials = true;
handler.PreAuthenticate = true;
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
client = new HttpClient(handler);
client.BaseAddress = new Uri(Properties.Settings.Default.WebApiUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var apiAccountLogin = apiManager.ApiAccountLogin(Properties.Settings.Default.WebApiPassword, Properties.Settings.Default.WebApiUsername);
var response = await client.PostAsJsonAsync(apiAccountLogin.Url, apiAccountLogin.Container);
response.EnsureSuccessStatusCode();
tokenModel = await response.Content.ReadAsAsync<TokenModel>();
return tokenModel;
}
But if I use void
then it is working fine
Foo()
{
IsLoginOk();
}
public async void IsLoginOk()
{
var handler = new HttpClientHandler();
handler.UseDefaultCredentials = true;
handler.PreAuthenticate = true;
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
client = new HttpClient(handler);
client.BaseAddress = new Uri(Properties.Settings.Default.WebApiUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var apiAccountLogin = apiManager.ApiAccountLogin(Properties.Settings.Default.WebApiPassword, Properties.Settings.Default.WebApiUsername);
var response = await client.PostAsJsonAsync(apiAccountLogin.Url, apiAccountLogin.Container);
response.EnsureSuccessStatusCode();
tokenModel = await response.Content.ReadAsAsync<TokenModel>();
}
As I see my code is like Microsift example code http://msdn.microsoft.com/en-us/library/hh191443.aspx .
Is this kind of some Deadlock?
I need to return tokenModel. How I can do this? Thanks!
Upvotes: 2
Views: 3207
Reputation: 456417
Your code is causing a deadlock that I explain on my blog. In short, when await
pauses an async
method, by default it will capture a context (e.g., a UI context or ASP.NET request context). Later, when the await
completes, the async
method is resumed in that context. However, if the calling code uses Task.Wait
or Task<T>.Result
to synchronously block on the async
method, then it is blocking a thread within that context and the async
method cannot complete.
The proper solution is to use async
all the way, not use async void
or ConfigureAwait(false)
:
async Task FooAsync()
{
var result = await IsLoginOkAsync();
}
public async Task<TokenModel> IsLoginOkAsync();
Upvotes: 1
Reputation: 176896
I am not sure but your code should be like this
This is one way to do it
public async void Foo()
{
var r = await IsLoginOk();
}
Way to do it make use of ConfigureAwait(false)
like as below when making call.
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
For this behaviour I searched on Google and found this good article. Please read it to avoid future problems: Don't Block on Async Code
if code is like this
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}
that it hangs So this is what happens, starting with the top-level method (Button1_Click for UI):
Upvotes: 2
Reputation: 1125
Change your constructor like this:
private Foo()
{
// Do only synchronous initialization here
}
public static async Task<Foo> CreateAsync()
{
var foo = new Foo();
await foo.IsLoginOk();
return foo;
}
Your callside needs to be changed accordingly.
Upvotes: 2
Reputation: 15097
IsLoginOk() will return a Task. To return a TokenModel alone, use
var r = IsLoginOk().Result;
If you don't wait for the result, execution will continue, as it is async. Also, Name should be IsLoginAsync()
Upvotes: -1