今天面试回来,面试题中有一道题如下:
根据线程安全的相关知识,分析以下代码,当调用test方法时i>10时是否会引起死锁?并简要说明理由。
- C# code
public void Test(int i){ lock (this) { if (i > 10) { i--; Test(i); } }}
网上有很多对于此题的回答,无非都是:
不会发生死锁,(但有一点int是按值传递的,所以每次改变的都只是一个副本,因此不会出现死锁。但如果把int换做一个object,那么死锁会发生)
我同样照此写在题上,但是面试官却告诉我会发生死锁,而且括号里面的解释也是错误的,然后就给我将原因,因为平时没用过此关键字,所以对于面试官的解释我听得迷迷糊糊。。回家后测试,测试代码如下:
- C# code
public void Test(int i){ Console.WriteLine("TestLockBegin"); lock (this) { if (i > 10) { i--; Test(i); } } Console.WriteLine("TestLockEnd");} //然后再调用Test(13);
返回的结果是:
TestLockBegin
TestLockBegin
TestLockBegin
TestLockEnd
TestLockEnd
TestLockEnd
我就搞不清楚了,这个分明就是没死锁嘛,难道是面试官整错了还是我对死锁没理解?还有网上说把int换成object会死锁,我测试了,得到和上面一样的返回结果,什么原因呢?求详解。。
------解决方案--------------------------------------------------------
lock的初衷是Atomicity
在类的成员方法内部使用 lock (this) ,无疑是将Atomicity 扩大了,因为只是要确保方法本身的一致性,而lock (this)则将一致性提到了类实例的层面。
lock语句实际上是调用 Monitor 的 Enter 和 Exit,同一线程在不阻止的情况下多次调用 Enter 是合法的
------解决方案--------------------------------------------------------
这都没有多线程
死什么锁
------解决方案--------------------------------------------------------
------解决方案--------------------------------------------------------
前提条件是考虑多线程。同时,互锁和死锁是不同的概念。比如说这个程序是封装在类型TA内的,写一个测试程序来说明一种多线程场景(这里我很多余地给出问题的人设计一种基于同一个对象实例来进行多线程调用的假设):
- C# code
var obj = new TA();var input=50;for(var i=0; i<10; i++){ ThreadPool.QueueUserWorkItem(h => { obj.Test(input); });}
------解决方案--------------------------------------------------------
由于出题者过于草率,我们就可以把它往无厘头的方向去想:
- C# code
var obj = new TA();var input = 50;ThreadPool.QueueUserWorkItem(h =>{ lock (obj) while (true) { };});for (var i = 0; i < 4; i++) ThreadPool.QueueUserWorkItem(h => { obj.Test(input); });