C# Winform:为什么这个异步使用不会陷入死锁状态?
C# Winform:为什么这个异步使用不会陷入死锁状态?
我正在研究async/await
。
我在C# winform: what's wrong in this async example if access result before await上有一个问题。
上面的问题已经解决了。这是一个死锁问题。
但是当我在stackoverflow上搜索一些问题时,我发现了这个:
async/await different thread ID
这也应该是一个死锁问题,对吗?
我重写了一个类似的代码进行测试:
private async void button1_Click(object sender, EventArgs e) { TB_Log(tbMsg, "click start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); Task task = func1(); TB_Log(tbMsg, "click wait, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); await task; TB_Log(tbMsg, "click end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); } private async Task func1() { TB_Log(tbMsg, "func1 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Run(() => { TB_Log(tbMsg, "func1 task.run, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); Tasktask = func2(); if (task.Result == 5) { ; } }); TB_Log(tbMsg, "func1 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); } private async Task func2() { TB_Log(tbMsg, "func2 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Run(() => { TB_Log(tbMsg, "func2 task.run, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); }); TB_Log(tbMsg, "func2 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); return 1; }
代码运行良好,不会阻塞。
代码结果显示:
click start, thread id:1 func1 start, thread id:1 click wait, thread id:1 func1 task.run, thread id:3 func2 start, thread id:3 func2 task.run, thread id:4 func2 end, thread id:4 func1 end, thread id:1 click end, thread id:1
task.Result
不会发生死锁。
我的问题是:
- 为什么
task.Result
不会导致死锁? - 为什么func1 end的线程ID是1,而不是4或3?
关于第二个问题,我打开了一个控制台项目来替换Winform项目。
结果有点不同。
static async Task Main(string[] args) { Console.WriteLine("Main Start, thread id:" + Thread.CurrentThread.ManagedThreadId); Task task = func1(); await task; Console.WriteLine("Main End, thread id:" + Thread.CurrentThread.ManagedThreadId); Console.ReadKey(); } static private async Task func1() { Console.WriteLine("func1 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Run(() => { Console.WriteLine("func1 task.run, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); Tasktask = func2(); if (task.Result == 5) { ; } }); Console.WriteLine("func1 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); } static private async Task func2() { Console.WriteLine("func2 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Run(() => { Console.WriteLine("func2 task.run, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); }); Console.WriteLine("func2 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString()); return 1; }
Main Start, thread id:1 func1 start, thread id:1 func1 task.run, thread id:4 func2 start, thread id:4 func2 task.run, thread id:5 func2 end, thread id:5 func1 end, thread id:4 Main End, thread id:4
Main End的ID还不是1。
非常感谢你。因为这真的让我困惑。
C# Winform: 为什么这个异步使用不会陷入死锁状态?
在这个问题中,死锁的关键问题是什么?
问题是在UI线程(点击处理程序)中访问Task.Result,并阻塞UI线程,而任务的继续部分(return 1;
)也需要在UI线程上运行,但是由于被阻塞,因此无法执行。
在这个代码示例中,您是否在UI线程中访问Task.Result?不是的。所以,没有死锁,可以吃蛋糕...(所有直接访问某个Task.Result的操作都在通过Task.Run执行并在后台线程池中运行的一些任务/函数中进行,因此不涉及UI线程。)
您在控制台应用程序中观察到的差异完全与同步上下文有关。简而言之,同步上下文确定任务的继续部分(继续部分是在异步函数中在等待的任务之后直到下一个等待的任务或函数结束之前执行的代码片段)将在哪个线程上运行。同步上下文在控制台应用程序和GUI应用程序中的确定方式是不同的。您可以在这里阅读有关所有这些脏衣服的内容:https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/