如果异步调用不一定在不同的线程上执行,阻止异步调用如何导致死锁?



我最近读了Stephen Cleary的文章,内容涉及当我们在同步方法中调用异步代码时可能发生的死锁:https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

关于这里的一个稍微修改过的例子(我添加的只是一个写行代码):

// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
Console.WriteLine("Before await");
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(true);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}

他的解释是,顶级方法正在阻止UI线程等待GetJsonAsync完成,而GetJsonAsync正在等待UI线程释放以便完成执行。

我的问题是,GetJsonAsync不是已经在UI线程上了吗?为什么它需要等待它被释放?根据这篇文章,调用异步函数不一定会为该方法创建另一个线程来执行。那么,如果GetJsonAsync一直在UI线程上执行,它会如何导致UI线程出现问题呢?就像执行Console.WriteLine()时,如果不是在UI线程上,这是在哪里完成的?我觉得我在这里错过了什么,但不知道是什么。

澄清:执行在什么时候离开UI线程/上下文并需要返回?有很多关于需要返回的讨论,但当它离开线程/上下文时,就永远不会了。

我要问的是,当从Button1_Click调用GetJsonAsync时,它在哪里执行?如果这个调用没有为它创建一个新的线程来执行。在GetJson异步中等待之前,它不是在执行控制台吗。是否仍在UI上下文中写入行(…)?

我建议阅读我的async简介。总结:

每个异步方法都开始同步执行。此代码:

public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}

调用UI线程上的GetJsonAsync,并且它确实开始在UI线程上执行。它在UI线程上执行Console.WriteLinenew在UI线程中建立客户端,甚至在UI线程调用GetStringAsync。它从该方法中获取一个任务,然后await执行它(为了简单起见,我忽略了ConfigureAwait(true))。

await是事物可能变得异步的点。任务尚未完成(即客户端尚未收到字符串),因此GetJsonAsync向其调用方返回一个未完成的任务。然后Button1_Click阻塞UI线程,等待该任务完成(通过调用.Result)。

因此,状态当前为GetJsonAsync不再在UI线程上运行。它实际上并不是";运行";在任何地方

稍后,当该字符串结果到达时,从GetStringAsync返回的任务完成,并且GetJsonAsync需要继续执行。它还没有出现在UI线程上;现在不在任何地方。由于await捕获了一个UI上下文,它将尝试在该上下文上(在UI线程上)恢复。

相关内容

  • 没有找到相关文章

最新更新