Async / Await和Task <t>不返回数据</t>

时间:2015-01-16 12:31:48

标签: c# .net async-await task-t

当我执行此操作时,它不会返回Task<TokenModel>我的意思是此行已冻结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;
}

但是如果我使用void那么它工作正常

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

我看到我的代码就像Microsift示例代码http://msdn.microsoft.com/en-us/library/hh191443.aspx

这是一种死锁

我需要返回 tokenModel 。我怎么能这样做? 谢谢!

4 个答案:

答案 0 :(得分:3)

像这样更改构造函数:

private Foo()
{
    // Do only synchronous initialization here
}

public static async Task<Foo> CreateAsync()
{
    var foo = new Foo();
    await foo.IsLoginOk();
    return foo;
}

您的呼叫方需要相应更改。

答案 1 :(得分:2)

我不确定,但您的代码应该是这样的

  1. 这是一种方法

    public async void Foo()
    {
      var r = await IsLoginOk();
    }
    
  2. 这样做的方法是在拨打电话时使用ConfigureAwait(false),如下所示。

    var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
    

  3. 对于这种行为,我在Google上搜索并找到了这篇好文章。请阅读以避免将来出现问题:Don't Block on Async Code

    死锁原因

    如果代码是这样的

    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;
    }
    

    它挂起所以这就是发生的事情,从顶级方法开始(Button1_Click for UI):

    1. 顶级方法调用GetJsonAsync(在UI上下文中)。
    2. GetJsonAsync通过调用HttpClient.GetStringAsync(仍然在上下文中)启动REST请求。
    3. GetStringAsync返回未完成的任务,表示REST请求未完成。
    4. GetJsonAsync等待GetStringAsync返回的任务。捕获上下文,稍后将用于继续运行GetJsonAsync方法。 GetJsonAsync返回未完成的Task,表示GetJsonAsync方法未完成。
    5. 顶级方法同步阻止GetJsonAsync返回的任务。这会阻止上下文线程。
    6. ...最终,REST请求将完成。这样就完成了GetStringAsync返回的任务。
    7. GetJsonAsync的延续现在已准备好运行,它等待上下文可用,以便它可以在上下文中执行。
    8. 死锁。顶级方法是阻塞上下文线程,等待GetJsonAsync完成,GetJsonAsync正在等待上下文空闲,以便它可以完成。

答案 2 :(得分:1)

您的代码是causing a deadlock that I explain on my blog。简而言之,当await暂停async方法时,默认情况下它将捕获上下文(例如,UI上下文或ASP.NET请求上下文)。稍后,当await完成时,async方法将在该上下文中恢复。但是,如果调用代码使用Task.WaitTask<T>.Result同步阻止async方法,则它会阻止该上下文中的线程,并且async方法无法完成。

正确的解决方案是一直使用async,而不是使用async voidConfigureAwait(false)

async Task FooAsync()
{
  var result = await IsLoginOkAsync();
}

public async Task<TokenModel> IsLoginOkAsync();

答案 3 :(得分:-1)

IsLoginOk()将返回一个任务。 要单独返回TokenModel,请使用

var r = IsLoginOk().Result;

如果您不等待结果,则执行将继续,因为它是异步的。此外,Name应为IsLoginAsync()