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()
我不确定,但你的代码应该是这样的
这是一种方法
public async void Foo()
{
var r = await IsLoginOk();
}
方法是在拨打电话时使用 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):
- 顶级方法调用 GetJsonAsync(在 UI 上下文中)。
- GetJsonAsync 通过调用 HttpClient.GetStringAsync(仍在上下文中)启动 REST 请求。
- GetStringAsync returns 一个未完成的任务,表示 REST 请求未完成。
- GetJsonAsync 等待 GetStringAsync 返回的任务。上下文已捕获,稍后将用于继续 运行 GetJsonAsync 方法。 GetJsonAsync returns 一个未完成的任务,表明 GetJsonAsync 方法未完成。
- 顶级方法同步阻止 GetJsonAsync 返回的任务。这会阻塞上下文线程。
- …最终,REST 请求将完成。这样就完成了 GetStringAsync 返回的任务。
- GetJsonAsync 的延续现在已准备好 运行,它等待上下文可用以便它可以在上下文中执行。
- 僵局。顶级方法正在阻塞上下文线程,等待 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.Wait
或 Task<T>.Result
同步阻塞 async
方法,那么它会阻塞该上下文中的线程,并且 async
方法无法完成。
正确的解决方案是一路使用async
,而不是使用async void
或ConfigureAwait(false)
:
async Task FooAsync()
{
var result = await IsLoginOkAsync();
}
public async Task<TokenModel> IsLoginOkAsync();
当我这样做时它没有 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()
我不确定,但你的代码应该是这样的
这是一种方法
public async void Foo() { var r = await IsLoginOk(); }
方法是在拨打电话时使用
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):
- 顶级方法调用 GetJsonAsync(在 UI 上下文中)。
- GetJsonAsync 通过调用 HttpClient.GetStringAsync(仍在上下文中)启动 REST 请求。
- GetStringAsync returns 一个未完成的任务,表示 REST 请求未完成。
- GetJsonAsync 等待 GetStringAsync 返回的任务。上下文已捕获,稍后将用于继续 运行 GetJsonAsync 方法。 GetJsonAsync returns 一个未完成的任务,表明 GetJsonAsync 方法未完成。
- 顶级方法同步阻止 GetJsonAsync 返回的任务。这会阻塞上下文线程。
- …最终,REST 请求将完成。这样就完成了 GetStringAsync 返回的任务。
- GetJsonAsync 的延续现在已准备好 运行,它等待上下文可用以便它可以在上下文中执行。
- 僵局。顶级方法正在阻塞上下文线程,等待 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.Wait
或 Task<T>.Result
同步阻塞 async
方法,那么它会阻塞该上下文中的线程,并且 async
方法无法完成。
正确的解决方案是一路使用async
,而不是使用async void
或ConfigureAwait(false)
:
async Task FooAsync()
{
var result = await IsLoginOkAsync();
}
public async Task<TokenModel> IsLoginOkAsync();