为什么 ThreadPool 决定使用调用 Task.Wait 的确切上下文线程?
Why ThreadPool decides to use the exact context thread which called Task.Wait?
为什么 ThreadPool 决定使用调用 Task.Wait
的确切上下文线程?
有the question。由于某些原因,我没有看到问题及其任何答案的评论按钮。所以,我在一个单独的线程中问一个相关的问题。
在链接的问题中有一个指向 the blog 的答案。根据此博客,以下声明成立。
有代码块:
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
// (real-world code shouldn't use HttpClient in a using block; this is just example code)
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;
}
还有死锁发生的解释:
- The top-level method calls
GetJsonAsync
(within the UI/ASP.NET context).
GetJsonAsync
starts the REST request by calling HttpClient.GetStringAsync
(still within the context).
GetStringAsync
returns an uncompleted Task
, indicating the REST request is not complete.
GetJsonAsync
awaits the Task
returned by GetStringAsync
. The context is captured and will be used to continue running the GetJsonAsync
method later. GetJsonAsync
returns an uncompleted Task, indicating that the GetJsonAsync
method is not complete.
- The top-level method synchronously blocks on the Task returned by
GetJsonAsync
. This blocks the context thread.
- … Eventually, the REST request will complete. This completes the
Task
that was returned by GetStringAsync
.
- The continuation for
GetJsonAsync
is now ready to run, and it waits for the context to be available so it can execute in the context.
- Deadlock. The top-level method is blocking the context thread, waiting for
GetJsonAsync
to complete, and GetJsonAsync
is waiting for the context to be free so it can complete.
我的问题特别是关于第 7 步。为什么 ThreadPool
决定使用一个阻塞的线程并等待它解除阻塞以请求该线程 运行 代码?为什么不直接使用其他线程?
GetJsonAsync
不在任意线程池线程上执行。它在上下文线程上执行。
根据示例代码,任务 GetJsonAsync
由按钮单击事件创建,该事件在 UI 线程(上下文线程)上执行。等待任务时,捕获当前上下文(在本例中为 UI 同步上下文)。任务完成后,继续在同一上下文中恢复。
第7步,任务试图return到UI线程,但是UI线程被.Result
阻塞,等待任务return。所以死锁发生了。
我注意到引用的问题是询问 ASP.NET WebApi 应用程序。所以只想澄清几点:
ASP.Net WebAPI 确实有一个特殊的同步上下文,但它不同于UI 上下文。只有一个 UI 线程,因此上下文仅将回调/延续安排到 UI 线程。
但是,ASP.Net WebAPI 的同步上下文不会捕获一个线程。 ASP.Net 代码可以在不同/任意线程上 运行。上下文负责恢复线程数据并确保延续以先到先得的方式链接在一起。
为什么 ThreadPool 决定使用调用 Task.Wait
的确切上下文线程?
有the question。由于某些原因,我没有看到问题及其任何答案的评论按钮。所以,我在一个单独的线程中问一个相关的问题。
在链接的问题中有一个指向 the blog 的答案。根据此博客,以下声明成立。
有代码块:
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
// (real-world code shouldn't use HttpClient in a using block; this is just example code)
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;
}
还有死锁发生的解释:
- The top-level method calls
GetJsonAsync
(within the UI/ASP.NET context).GetJsonAsync
starts the REST request by callingHttpClient.GetStringAsync
(still within the context).GetStringAsync
returns an uncompletedTask
, indicating the REST request is not complete.GetJsonAsync
awaits theTask
returned byGetStringAsync
. The context is captured and will be used to continue running theGetJsonAsync
method later.GetJsonAsync
returns an uncompleted Task, indicating that theGetJsonAsync
method is not complete.- The top-level method synchronously blocks on the Task returned by
GetJsonAsync
. This blocks the context thread.- … Eventually, the REST request will complete. This completes the
Task
that was returned byGetStringAsync
.- The continuation for
GetJsonAsync
is now ready to run, and it waits for the context to be available so it can execute in the context.- Deadlock. The top-level method is blocking the context thread, waiting for
GetJsonAsync
to complete, andGetJsonAsync
is waiting for the context to be free so it can complete.
我的问题特别是关于第 7 步。为什么 ThreadPool
决定使用一个阻塞的线程并等待它解除阻塞以请求该线程 运行 代码?为什么不直接使用其他线程?
GetJsonAsync
不在任意线程池线程上执行。它在上下文线程上执行。
根据示例代码,任务 GetJsonAsync
由按钮单击事件创建,该事件在 UI 线程(上下文线程)上执行。等待任务时,捕获当前上下文(在本例中为 UI 同步上下文)。任务完成后,继续在同一上下文中恢复。
第7步,任务试图return到UI线程,但是UI线程被.Result
阻塞,等待任务return。所以死锁发生了。
我注意到引用的问题是询问 ASP.NET WebApi 应用程序。所以只想澄清几点:
ASP.Net WebAPI 确实有一个特殊的同步上下文,但它不同于UI 上下文。只有一个 UI 线程,因此上下文仅将回调/延续安排到 UI 线程。
但是,ASP.Net WebAPI 的同步上下文不会捕获一个线程。 ASP.Net 代码可以在不同/任意线程上 运行。上下文负责恢复线程数据并确保延续以先到先得的方式链接在一起。