异步等待导致死锁
async wait leading to deadlock
我在页面加载上有一个异步代码块 运行ning。
代码 运行 平滑,直到您到达 capturevalue 方法,我们在该方法中创建一个新的 task.On 执行该代码块等待只是冻结,然后控制不会返回似乎代码刚刚进入死锁
protected void Page_Load(object sender, EventArgs e)
{
try
{
var textvalue = GetTextValueFromTask();
txtbox.Text = textvalue.Result;
string ss = "";
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private async Task<string> GetTextValueFromTask()
{
string strReturn = await CaptureValue();
return strReturn;
}
private async Task<string> CaptureValue()
{
Thread.Sleep(5000);
Task<string> T = Task.Factory.StartNew<string>(() => "hi");
return await T;
}
然后我在Capturevalue方法上做了一个小改动
private async Task<string> CaptureValue()
{
Thread.Sleep(5000);
Task<string> T = Task.Factory.StartNew<string>(() => "hi");
string ss = T.Result;
return await T;
}
我做出更改后,它开始工作 normal.What 不同之处在于它最初只是获取结果。请帮助我我是异步的新手
不同的是第二次没有发生任何事情 "await" 因为你自己等待任务,所以 await
什么都不做。
我想你第一次错过了 await 关键字,在这里:
var textvalue = await GetTextValueFromTask();
如果没有它,您的方法 GetTextValueFromTask
将同步运行,然后它进入 CaptureValue
方法,其中 await
发生。但是 await
的默认行为是它尝试捕获调用它的同步上下文并在该上下文中继续该方法的其余部分,在您的示例中它是 WPF 同步上下文,它不允许超过一个线程一次执行。但是continuation无法进行,因为context已经被await机制使用了。
所以,再来一次。有一个线程(UI 线程)执行您的代码直到最后一次等待,即 return await T;
,然后返回给调用者 - GetTextValueFromTask
,然后返回到 Page_Load
当它被阻塞时,因为最初你调用 GetTextValueFromTask
同步(没有等待)。之后,您的操作 T
完成,并且您的代码尝试使用初始同步上下文(WPF 上下文)继续执行。但它不能,因为它已经在 Page_Load
.
中等待了
评论里的link描述的比较详细
还请考虑在 async/await 场景中不要使用 Thread.Sleep
,因为它会破坏代码的所有 "asynchronous" 特性。延伸阅读:link.
另一条不直接适用于您的源代码的一般建议是不要使用 Task.Factory.StartNew
,而是使用 Task.Run
。说明 here.
请使用 Task.Run() 而不是 Task.Factory.StartNew()
var T = Task.Run(() => "hi");
如何处理这个任务由 Task.Run 决定。
另外请在您的等待调用中使用 .ConfigureAwait(false),它不需要在等待线程上下文中完成继续。
我在页面加载上有一个异步代码块 运行ning。 代码 运行 平滑,直到您到达 capturevalue 方法,我们在该方法中创建一个新的 task.On 执行该代码块等待只是冻结,然后控制不会返回似乎代码刚刚进入死锁
protected void Page_Load(object sender, EventArgs e)
{
try
{
var textvalue = GetTextValueFromTask();
txtbox.Text = textvalue.Result;
string ss = "";
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private async Task<string> GetTextValueFromTask()
{
string strReturn = await CaptureValue();
return strReturn;
}
private async Task<string> CaptureValue()
{
Thread.Sleep(5000);
Task<string> T = Task.Factory.StartNew<string>(() => "hi");
return await T;
}
然后我在Capturevalue方法上做了一个小改动
private async Task<string> CaptureValue()
{
Thread.Sleep(5000);
Task<string> T = Task.Factory.StartNew<string>(() => "hi");
string ss = T.Result;
return await T;
}
我做出更改后,它开始工作 normal.What 不同之处在于它最初只是获取结果。请帮助我我是异步的新手
不同的是第二次没有发生任何事情 "await" 因为你自己等待任务,所以 await
什么都不做。
我想你第一次错过了 await 关键字,在这里:
var textvalue = await GetTextValueFromTask();
如果没有它,您的方法 GetTextValueFromTask
将同步运行,然后它进入 CaptureValue
方法,其中 await
发生。但是 await
的默认行为是它尝试捕获调用它的同步上下文并在该上下文中继续该方法的其余部分,在您的示例中它是 WPF 同步上下文,它不允许超过一个线程一次执行。但是continuation无法进行,因为context已经被await机制使用了。
所以,再来一次。有一个线程(UI 线程)执行您的代码直到最后一次等待,即 return await T;
,然后返回给调用者 - GetTextValueFromTask
,然后返回到 Page_Load
当它被阻塞时,因为最初你调用 GetTextValueFromTask
同步(没有等待)。之后,您的操作 T
完成,并且您的代码尝试使用初始同步上下文(WPF 上下文)继续执行。但它不能,因为它已经在 Page_Load
.
评论里的link描述的比较详细
还请考虑在 async/await 场景中不要使用 Thread.Sleep
,因为它会破坏代码的所有 "asynchronous" 特性。延伸阅读:link.
另一条不直接适用于您的源代码的一般建议是不要使用 Task.Factory.StartNew
,而是使用 Task.Run
。说明 here.
请使用 Task.Run() 而不是 Task.Factory.StartNew()
var T = Task.Run(() => "hi");
如何处理这个任务由 Task.Run 决定。
另外请在您的等待调用中使用 .ConfigureAwait(false),它不需要在等待线程上下文中完成继续。