避免 Task.Run 导致主线程死锁(C#)

Posted

技术标签:

【中文标题】避免 Task.Run 导致主线程死锁(C#)【英文标题】:Avoiding Task.Run causing deadlock on Main thread (C#) 【发布时间】:2021-08-02 17:05:09 【问题描述】:

我相信,虽然不太可能,但使用 Task.Run() 排队的任务有可能最终在主线程上运行。

我担心(在我的 WPF 应用程序中),如果我在此任务中使用 App.Current.Dispatcher.Invoke() 或类似名称,我会导致死锁。

这是一种正当的恐惧吗?如果是这样,有没有办法防止这种情况发生(不检查当前线程*不是*主线程)。

谢谢

【问题讨论】:

【参考方案1】:

我相信,虽然不太可能,但使用 Task.Run() 排队的任务有可能最终在主线程上运行。

您错误地认为:这是不可能的。 Task.Run 分派到 ThreadPool,而不是 UI 线程的分派器。两者是完全分开的。

我担心(在我的 WPF 应用程序中),如果我在此任务中使用 App.Current.Dispatcher.Invoke() 或类似方法,我会导致死锁。

无论如何,您都应该避免使用Invoke,因为存在死锁的风险。风险在于 UI 线程正在等待您的线程做某事,并且通过同步等待 UI 线程有足够的空闲来处理您的消息,您就有死锁的风险。

您通常可以使用 BeginInvoke 代替(拥有一个其进度取决于 UI 正在执行的操作的后台线程通常是不好的设计),但是当您使用 awaitProgress<T>/@ 时,您很少需要它987654326@.

【讨论】:

我注意到在 TPL 中使用 Parallel.ForEach 通常会在“主”线程上运行一个线程。是的,这个调用阻塞了调用线程,所以使用这个“备用”线程是有意义的。这是一些 TPL 的“魔法”吗?正如 MSDN 所说,它从 ThreadPool 获得分配。 Parallel.ForEach 将使用它被调用的线程,并根据需要加上任意数量的额外线程。如果您从 UI 线程调用它,是的,它将使用 UI 线程。如果你从非 UI 线程调用它,它不会使用 UI 线程。

以上是关于避免 Task.Run 导致主线程死锁(C#)的主要内容,如果未能解决你的问题,请参考以下文章

C#使用异步操作时的注意要点(翻译)

C# -Task.Run() 中线程池中的索引超出范围异常

Java开发之——线程面试篇:死锁和如何避免死锁?

避免活跃性

如何避免线程的死锁

C# 死锁的原理与排查方法详解