lock死锁

c#中有个关键字lock,它的作用是锁定某一代码块,让同一时间只有一个线程访问该代码块,下面就我个人而言谈谈lock关键字的原理和其中应注意的几个问题: 
lock的使用原型是: 
lock(X) 
{ 
    //需要锁定的代码.... 
} 
首先要明白为什么上面这段话能够锁定代码,其中的奥妙就是X这个对象,事实上X是任意一种引用类型,它在这儿起的作用就是任何线程执行到lock(X)时候,X需要独享才能运 

行下面的代码,若假定现在有3个线程A,B,C都执行到了lock(X)而ABC因为此时都占有X,这时ABC就要停下来排个队,一个一个使用X,从而起到在下面的代码块内只有一个线程在 

运行(因为此时只有一个线程独享X,其余两个在排队),所以这个X必须是所有要执行临界区域代码进程必须共有的一个资源,从而起到抑制线程的作用。 

下面再来谈谈lock使用中会遇到和注意的问题,lock最需要注意的一个问题就是线程死锁! 
在MSDN上列出了3个典型问题: 
通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则: 

如果实例可以被公共访问,将出现 lock (this) 问题。 

如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。 

由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。 

最佳做法是定义 private 对象来锁定, 或 private shared 对象变量来保护所有实例所共有的数据。 

(1)lock (this) 问题: 
假定有两个类: 
class A{} 
class B{} 

有两个公共对象: 
A a=new A(); 
B b=new B(); 

首先在A中若有一函数内的代码需要锁定: 
代码1: 
lock(this)//this在这里就是a 
{ 
  //.... 
  lock(b) 
  { 
//...... 
  } 
} 

然而此时B中某函数也有如下代码需要锁定: 
代码2: 
lock(this)//this在这里就是b 
{ 
  //.... 
  lock(a) 
  { 
//...... 
  } 
} 

设想一下上面两段代码在两个线程下同时执行会有什么后果? 
结果就是,代码1执行到lock(this)后a被锁定,代码2执行到lock(this)后b被锁定,然后代码1需求b,代码2需求a,此时两个需求都被相互占有出现僵持状态,程序死锁了.... 


(2)lock (typeof (MyType)) 问题: 
假定有两个公共变量 
int a;float b; 
下面看如下代码 
代码3: 
lock(typeof(a))//typeof(a)就是System.type.Int类型 
{ 
  //.... 
  lock(typeof(b)) 
  { 
//...... 
  } 
} 

又有如下代码: 
代码4: 
lock(typeof(b))//typeof(b)就是System.type.Float类型 
{ 
  //.... 
  lock(typeof(a)) 
  { 
//...... 
  } 
} 

若有两个进程分别同时进入上面两个代码外层的lock,就分别锁定了System.type.Int和System.type.Float,而马上它们又需求System.type.Float和System.type.Int,彼此相互 

占有,彼此僵持,程序进入死锁状态! 


(3)字符串问题 : 
在阐述这个问题之前,有一个知识大家必须知道:C#中字符串被公共语言运行库 (CLR)“暂留”。这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了 

所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。 
言下之意就是假定有两个类分别有两个字符串: 
class A 
{ 
  string a="abc"; 
  string b="def"; 
} 

class c 
{ 
  string c="abc"; 
  string d="def"; 
} 
事实上a和c引用的是同一个字符串"abc",b和d引用的是同一个字符串"def" 
现在如果在两个类中有如下代码 
在类A中 
有代码5: 
lock(b)//b是"def" 
{ 
  //.... 
  lock(a)//a是"abc" 
  { 
//...... 
  } 
} 

在类B中 
有代码6: 
lock(c)//c是"abc" 
{ 
  //.... 
  lock(d)//d是"def" 
  { 
//...... 
  } 
} 
那么代码5和代码6同时有两个线程执行结果可想而知:在两个线程执行到外层lock代码时"def"和"abc"被锁定。接着他们在内部lock处同时需求"abc"和"def",而此时两个字符串 

被两个进程彼此占有,程序又死锁了!所以MSDN说:锁定字符串尤其危险!最好不要使用! 

MSDN最后说了:最佳做法是定义 private 对象来锁定, 或 private shared 对象变量来保护所有实例所共有的数据。 
在我个人看来,也不是绝对安全,这里我就举出一个例子: 
假定有一个类: 
class A 
{ 
  private Object a=new Object(); 
  private Object b=new Object(); 

  public void x() 
  { 
    lock(a) 
    { 
        //..... 
        lock(b) 
        { 
          //.... 
        } 
    } 
  } 
  
  public void y() 
  { 
    lock(b) 
    { 
        //..... 
        lock(a) 
        { 
          //.... 
        } 
    } 
  } 
} 

现在假定有两个线程同时执行函数x()和y();结果private对象a和b分别在外层lock锁定,接着两个线程在内部又立马需求b和a,a,b彼此占有又彼此需求,程序死锁。 


所以具体要看情况而定,但是定义 private 对象来锁定至少可以降低风险. 
转:<a target=_blank href="http://blog.youkuaiyun.com/shellwin/article/details/5855504">http://blog.youkuaiyun.com/shellwin/article/details/5855504</a>
C#中,`lock`关键字用于确保多个线程在访问共享资源时不会发生冲突。`lock`关键字可以防止多个线程同时进入同一段代码块,从而避免数据竞争和不一致。然而,不正确地使用`lock`可能导致死锁、阻塞和卡死等问题。 ### 死锁 死锁是指两个或多个线程互相等待对方释放资源,导致所有线程都无法继续执行。例如,线程A持有资源1并请求资源2,而线程B持有资源2并请求资源1,此时就会发生死锁。 ### 阻塞 阻塞是指线程在等待某个条件满足时被暂停执行。例如,线程在等待某个被释放时会被阻塞。 ### 卡死 卡死是指程序因为某些原因无法继续执行,通常是由于死锁或无限循环等原因导致的。 ### 避免死锁的方法 1. **按顺序获取**:确保所有线程按相同的顺序获取。例如,总是先获取资源1的,再获取资源2的。 2. **使用超时**:在获取时使用超时机制,如果超时则释放已获取的并重试。例如,使用`Monitor.TryEnter`方法。 3. **最小化的作用域**:尽量减少的作用域,只在必要时加。 4. **使用`Mutex`或`Semaphore`**:在复杂的情况下,可以使用更高级的同步原语来管理。 ### 示例代码 ```csharp private static readonly object lock1 = new object(); private static readonly object lock2 = new object(); public void ThreadMethod1() { lock (lock1) { Thread.Sleep(1000); // 模拟一些工作 lock (lock2) { // 执行一些操作 } } } public void ThreadMethod2() { lock (lock2) { Thread.Sleep(1000); // 模拟一些工作 lock (lock1) { // 执行一些操作 } } } ``` 在这个示例中,`ThreadMethod1`和`ThreadMethod2`分别按不同的顺序获取,可能导致死锁。 ### 解决方案 可以通过按相同顺序获取来避免死锁: ```csharp public void ThreadMethod1() { lock (lock1) { Thread.Sleep(1000); // 模拟一些工作 lock (lock2) { // 执行一些操作 } } } public void ThreadMethod2() { lock (lock1) { Thread.Sleep(1000); // 模拟一些工作 lock (lock2) { // 执行一些操作 } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值