如果异步调用不一定在不同的线程上执行,那么阻塞异步调用如何导致死锁?
How can blocking async calls cause a deadlock if async calls aren't necessarily executed on a different thread?
我最近阅读了 Stephen Cleary 的 post 关于我们在此处以同步方法调用异步代码时可能发生的死锁:https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
关于此处稍微修改的示例(我添加的只是一个 writeline 代码):
// 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;
}
他的解释是顶级方法正在阻塞等待 GetJsonAsync
完成的 UI 线程,而 GetJsonAsync
正在等待 UI 线程完成已释放,以便它可以完成执行。
我的问题是,GetJsonAsync
不是已经在 UI 线程上了吗?为什么需要等待它被释放?根据此 post here 调用异步函数不一定会为要执行的方法创建另一个线程。那么,如果 GetJsonAsync
一直在 UI 线程上执行,它会如何导致 UI 线程出现问题?就像执行 Console.WriteLine()
时,如果不在 UI 线程上,这是在哪里完成的?我觉得我在这里错过了什么,但不知道是什么。
澄清:执行在什么时候离开 UI thread/context 并需要 return?有很多关于需要 return 的讨论,但从来没有离开 thread/context.
What I'm asking is, where is GetJsonAsync executing when it's called from Button1_Click if this call doesn't create a new thread for it to execute on. Before the await within GetJsonAsync, isn't it executing the Console.WriteLine(...) within the UI context still?
我建议阅读我的 async
intro。总结:
每个异步方法开始同步执行。此代码:
public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}
在 UI 线程上调用 GetJsonAsync
,它确实开始在 UI 线程上执行。它在 UI 线程上执行 Console.WriteLine
,new
在 UI 线程上启动客户端,甚至在 UI 线程上调用 GetStringAsync
.它从该方法返回一个任务,然后 await
执行它(为简单起见,我忽略了 ConfigureAwait(true)
)。
await
是事物可能变得异步的点。任务未完成(即,客户端尚未收到字符串),因此 GetJsonAsync
returns 对其调用者来说是未完成的任务。然后 Button1_Click
阻塞 UI 线程,等待该任务完成(通过调用 .Result
)。
因此,当前状态 GetJsonAsync
不再是 UI 线程上的 运行。是not actually "running" anywhere.
稍后,当该字符串结果到达时,从GetStringAsync
返回的任务完成,GetJsonAsync
需要继续执行。它不在 UI 线程中;它现在不在任何地方。由于 await
捕获了 UI 上下文,它将尝试在该上下文(在 UI 线程上)上恢复。
我最近阅读了 Stephen Cleary 的 post 关于我们在此处以同步方法调用异步代码时可能发生的死锁:https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
关于此处稍微修改的示例(我添加的只是一个 writeline 代码):
// 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;
}
他的解释是顶级方法正在阻塞等待 GetJsonAsync
完成的 UI 线程,而 GetJsonAsync
正在等待 UI 线程完成已释放,以便它可以完成执行。
我的问题是,GetJsonAsync
不是已经在 UI 线程上了吗?为什么需要等待它被释放?根据此 post here 调用异步函数不一定会为要执行的方法创建另一个线程。那么,如果 GetJsonAsync
一直在 UI 线程上执行,它会如何导致 UI 线程出现问题?就像执行 Console.WriteLine()
时,如果不在 UI 线程上,这是在哪里完成的?我觉得我在这里错过了什么,但不知道是什么。
澄清:执行在什么时候离开 UI thread/context 并需要 return?有很多关于需要 return 的讨论,但从来没有离开 thread/context.
What I'm asking is, where is GetJsonAsync executing when it's called from Button1_Click if this call doesn't create a new thread for it to execute on. Before the await within GetJsonAsync, isn't it executing the Console.WriteLine(...) within the UI context still?
我建议阅读我的 async
intro。总结:
每个异步方法开始同步执行。此代码:
public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}
在 UI 线程上调用 GetJsonAsync
,它确实开始在 UI 线程上执行。它在 UI 线程上执行 Console.WriteLine
,new
在 UI 线程上启动客户端,甚至在 UI 线程上调用 GetStringAsync
.它从该方法返回一个任务,然后 await
执行它(为简单起见,我忽略了 ConfigureAwait(true)
)。
await
是事物可能变得异步的点。任务未完成(即,客户端尚未收到字符串),因此 GetJsonAsync
returns 对其调用者来说是未完成的任务。然后 Button1_Click
阻塞 UI 线程,等待该任务完成(通过调用 .Result
)。
因此,当前状态 GetJsonAsync
不再是 UI 线程上的 运行。是not actually "running" anywhere.
稍后,当该字符串结果到达时,从GetStringAsync
返回的任务完成,GetJsonAsync
需要继续执行。它不在 UI 线程中;它现在不在任何地方。由于 await
捕获了 UI 上下文,它将尝试在该上下文(在 UI 线程上)上恢复。