.NET 4.7.2网站
using System;
using System.Threading.Tasks;
using System.Web;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
string input = Input.Text;
bool iWantDeadlock = input == "yes";
string key = iWantDeadlock
? GetHashFragmentAsync(input).GetResultSafely()
: Task.Run(() => GetHashFragmentAsync(input)).Result;
Response.Redirect(key);
}
}
private static async Task<string> GetHashFragmentAsync(string key)
{
await Task.Delay(100);
return "#" + HttpUtility.UrlEncode(key);
}
}
public static class TaskExtensions
{
public static T GetResultSafely<T>(this Task<T> task)
{
return Task.Run(() => task).Result;
}
}
我制作了一个非常简单的页面,带有一个文本框,提交后将输入内容放入页面的哈希片段中。
我正在阅读“任务和死锁”(在我遇到其他导致一个死锁的代码之后)。
解决方案似乎是这样做的:
Task.Run(() => GetHashFragmentAsync(input)).Result;
所以我想,让我们做一个扩展以使其清晰易用。
public static class TaskExtensions
{
public static T GetResultSafely<T>(this Task<T> task)
{
return Task.Run(() => task).Result;
}
}
但是,这将导致死锁。代码是相同的,但工作方式却大不相同。 有人可以解释这种行为吗?
答案 0 :(得分:0)
因为使用GetHashFragmentAsync(input).GetResultSafely()
时实际上有2个任务而不是1个。
GetHashFragmentAsync(input)
返回已经开始的任务。然后调用GetResultSafely()
,这将创建另一个任务。
当您在UI线程上等待任务时,您会死锁,因为任务无法返回到同步上下文线程。当您有两个任务时,第二个任务可以同步等待,因为父任务不在UI线程上,而是在ThreadPool线程上,后者没有同步上下文。
调用任何基于IO的代码时,切勿调用Task.Run
。只需写
protected async void EventHandlerMethod(object sender, EventArgs e)
{
await GetHashFragmentAsync(input);
}
这是怎么回事
GetHashFragmentAsync(input) // Start Task number 1
.GetResultSafely() // Start task number 2 from task number 1 (no synchronization context)
// .Result returns back to task 1
第二种情况
Task.Run(() => GetHashFragmentAsync(input)) // UI thread so, capture synchronization context
.Result // Wait for UI thread to finish (deadlock)
答案 1 :(得分:-2)
调用GetHashFragmentAsync(input)
时,当前的同步上下文由C#异步/等待机制捕获。该方法返回取决于UI线程的已启动任务。您尝试使用Task.Run
移动关键UI线程的任务,但为时已晚。
GetHashFragmentAsync(input)
必须已经在非UI线程上被调用。将其包装在Task.Run
中。
以下是通过工厂建立工作的辅助方法:
public static T GetResultSafely<T>(Func<Task<T>> task)
{
return Task.Run(() => task()).Result;
}
在线程池上调用工厂。
我应该说,通常最好的解决方案是使用异步API,或者保持完全同步。出于正确性和性能的原因,混合是有问题的。但这绝对可以安全地完成。