【发布时间】:2017-08-07 19:27:50
【问题描述】:
Jeffrey Richter 在他的“CLR via C#”一书中指出了我不理解的可能死锁示例(第 702 页,带边框的段落)。
示例是一个线程,它运行 Task 并为此 Task 调用 Wait()。如果任务未启动,则 Wait() 调用应该没有阻塞,而是正在运行未启动的任务。如果在 Wait() 调用之前输入了锁,并且 Task 也尝试输入此锁,则可能导致死锁。
但是锁是在同一个线程中输入的,这是否会导致死锁场景?
以下代码产生预期的输出。
class Program
{
static object lockObj = new object();
static void Main(string[] args)
{
Task.Run(() =>
{
Console.WriteLine("Program starts running on thread {0}",
Thread.CurrentThread.ManagedThreadId);
var taskToRun = new Task(() =>
{
lock (lockObj)
{
for (int i = 0; i < 10; i++)
Console.WriteLine("{0} from Thread {1}",
i, Thread.CurrentThread.ManagedThreadId);
}
});
taskToRun.Start();
lock (lockObj)
{
taskToRun.Wait();
}
}).Wait() ;
}
}
/* Console output
Program starts running on thread 3
0 from Thread 3
1 from Thread 3
2 from Thread 3
3 from Thread 3
4 from Thread 3
5 from Thread 3
6 from Thread 3
7 from Thread 3
8 from Thread 3
9 from Thread 3
*/
没有发生死锁。
J。 Richter 在他的书“CLR via C#”第 4 版第 702 页上写道:
当线程调用Wait方法时,系统会检查线程等待的Task是否已经开始执行。如果有,则调用 Wait 的线程将阻塞,直到 Task 完成运行。但是如果Task还没有开始执行,那么系统可能(取决于TaskScheduler)使用调用Wait的线程来执行Trask。如果发生这种情况,那么调用 Wait 的线程不会阻塞;它执行任务并立即返回。这很好,因为没有线程被阻塞,从而减少了资源使用(通过不创建线程来替换阻塞的线程),同时提高了性能(没有时间来创建线程并且没有上下文切换)。但是如果,例如,线程在调用 Wait 之前已经获取了线程同步锁,并且任务尝试获取相同的锁,从而导致线程死锁,这也可能会很糟糕!
如果我理解正确的话,上面的代码必须以死锁结束!?
【问题讨论】:
-
@Noseratio:这不一样!关键是引用的段落描述了当一个线程重入锁时不应发生的死锁。在所描述的引用场景中是否可能出现死锁?
-
嗯,您似乎对
Task的工作原理不感兴趣,而是对如何通过尝试多次“锁定”资源来使单个线程自身死锁感兴趣。也许您问题的这一部分可以拆分为一个根本不引用Task/TPL 的新问题? ;-)
标签: c# .net task-parallel-library clr deadlock