emb*_*bee 8 .net c# clr deadlock task-parallel-library
杰弗里里希特在他的书"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
*/
Run Code Online (Sandbox Code Playgroud)
没有发生死锁.
J. Richter在第702页的第4版"CLR via C#"一书中写道:
当线程调用Wait方法时,系统会检查线程正在等待的Task是否已开始执行.如果有,则调用Wait的线程将阻塞,直到Task完成运行.但是如果Task还没有开始执行,那么系统可以(取决于TaskScheduler)通过使用调用Wait的线程来执行Trask.如果发生这种情况,则调用Wait的线程不会阻塞; 它执行任务并立即返回.这很好,因为没有线程被阻塞,从而减少了资源使用(通过不创建线程来替换被阻塞的线程),同时提高性能(没有时间来创建线程,没有上下文切换).但是,如果,例如,在调用Wait之前,线程已经采用线程同步锁定,并且任务尝试采用相同的锁定,那么它也可能会很糟糕,从而导致死锁线程!
如果我正确理解了段落,上面的代码必须以死锁结束!?
你太习惯了我对"锁"一词的使用.C#"lock"语句(我的书不鼓励使用),内部利用Monitor.Enter/Exit.Monitor锁是一个支持线程所有权和递归的锁.因此,单个线程可以成功多次获得这种锁.但是,如果你使用不同类型的锁,如信号量(Slim),AutoResetEvent(Slim)或ReaderWriterLockSlim(没有递归),那么当单个线程试图多次获取任何这些锁时,就会发生死锁.