简体   繁体   中英

Async/Await and Task<T> does not return data

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!

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.

I am not sure but your code should be like this

  1. This is one way to do it

     public async void Foo() { var r = await IsLoginOk(); } 
  2. 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

Reason of deadlock

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):

  1. The top-level method calls GetJsonAsync (within the UI context).
  2. GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
  3. GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
  4. GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.
  5. The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
  6. … Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
  7. The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
  8. Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete.

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 (eg, 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();

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM