Async/Await 和 Task<T> 没有 return 数据

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

当我这样做时它没有 return 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 .

这种是不是有些死锁

我需要returntokenModel。我该怎么做? 谢谢!

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

var r = IsLoginOk().Result;

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

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

  1. 这是一种方法

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

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

对于这种行为,我在 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 returns 一个未完成的任务,表示 REST 请求未完成。
  4. GetJsonAsync 等待 GetStringAsync 返回的任务。上下文已捕获,稍后将用于继续 运行 GetJsonAsync 方法。 GetJsonAsync returns 一个未完成的任务,表明 GetJsonAsync 方法未完成。
  5. 顶级方法同步阻止 GetJsonAsync 返回的任务。这会阻塞上下文线程。
  6. …最终,REST 请求将完成。这样就完成了 GetStringAsync 返回的任务。
  7. GetJsonAsync 的延续现在已准备好 运行,它等待上下文可用以便它可以在上下文中执行。
  8. 僵局。顶级方法正在阻塞上下文线程,等待 GetJsonAsync 完成,而 GetJsonAsync 正在等待上下文空闲以便完成。

像这样更改您的构造函数:

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

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

您的调用方需要相应地更改。

您的密码是 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();