lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,而后释放该锁。多线程
lock (xxx) { // Critical code section. }
lock 关键字可确保当一个线程位于代码的临界区时,另外一个线程不会进入该临界区。 若是其余线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。ide
用实例说话:post
例1字体
新建多个线程,用多个线程的操做来模拟实现lock的场景ui
public static void fun()
{
Thread[] threads = new Thread[500];
User u = new User();
for (int i = 0; i < 50; i++)
{
threads[i] = new Thread(new ParameterizedThreadStart(getInstance));
threads[i].IsBackground = true;
threads[i].Start(u);
}
}
以上新建了50个线程,而且让每一个线程都执行getInstance方法,而且每一个getInstance方法的参数为User的实例uthispublic static void getInstance(object obj)
{
User u = (User)obj;
Console.WriteLine(u.addtion());
}spa以上代码段执行User类的addtion方法,并获得返回值。即:此时有50个线程在执行同一个User实例的u的addtion方法,在执行addtion方法的时候,addtion方法中的lock(this)起到锁的做用,当第一个线程进入lock代码块中后,将会把其余的线程阻塞到外面,直到第一个线程执行完,锁获得释放,其余线程中的一个才得以执行。线程
public class User
{调试private static int sin;code
public string addtion()
{
lock (this)
{
sin = sin + 10;
Thread.Sleep(50);
return sin.ToString();
}
}
}
如上的代码,若是lock(this)起到锁的做用的话,线程一个一个执行,每一个线程的执行sin的值都会增长10。指望的效果为:10,20,30,40,50....
=====执行的效果为
:注意上述代码中的红色字体标记的部分,多个线程执行的都是同一个User实例的addtion方法,因此lock(this)起到了锁了做用,若是让每一个线程都实例化一个User类,再去执行本身实例化的User类的addtion方法,那么lock(this)就起不到做用了!
即:public static void fun()
{
Thread[] threads = new Thread[500];
for (int i = 0; i < 50; i++)
{
threads[i] = new Thread(new ParameterizedThreadStart(getInstance));
threads[i].IsBackground = true;
threads[i].Start(i);
}
}
public static void getInstance(object obj)
{
User u = new User();
Console.WriteLine(u.addtion());
}若是是上述代码的话,则lock就锁不住,lock块中可能有多个线程在执行,即:若是第一个线程把sin设置为10,再第一个线程尚未结束以前,第二个线程会再将sin加10,如此循环,n个线程执行以后,那么第一个线程执行结束后,sin的值就变成了n*10。
执行效果为:
实例中还涉及到静态字段,静态字段在IL中标记为BeforeFieldInit,即:静态字段是由程序自动执行,与普通字段不一样的是,它仅执行一次,在以后类的实例化时,也不须要再此执行。从而上述代码中sin的字段才得以保存。若是sin字段的static去掉的话,则输出的就是:10,10,10,10....
由此便可获得结论:lock(this) 锁定 当前实例对象,若是有多个类实例的话,lock锁定的只是当前类实例,对其它类实例无影响。
lock中的this表示的是当前实例,对于同一个实例的多线程来讲,当第一个现成进来的时,经过lock(this)将当前实例上锁,那么当此实例的其余线程进来的时候,该实例已经上锁,因此就不能进入;而对于每一个线程都实例化一个对象就不同了,当第一个线程进来的时候,lock将第一个线程的实例上锁,那么以后全部的第一个线程实例化的对象就不能再进入访问,这里仅仅是对第一个线程实例化的对象,若是是除此以外的全部其余的对象的话,是起不到锁的做用,即:当第二个线程、第三个线程、、等,到达lock时,是不被锁上的!
例2
既然lock(this)只能防止当前实例多线程访问,不能防止其余实例进入!对于上述的内容若是了解以后,不难想到如何作到绝对防止其余线程(不管是什么实例)进入lock的逻辑块,其实就来实例化一个任意的对象(静态的),例如:private static object obj=new object();lock(obj);由于静态的对象只是由程序自动执行一次,以后再实例化时,用得仍是原来的,下面就来讲一下访问流程,新建n个任意实例的线程,当第一个线程进来时,利用lock将obj上锁,第一个线程进入lock逻辑块中执行,当第二个、第三个等线程进来时,由于obj是静态的对象,全部obj的值仍是第一线程给设置的状态,即:是上锁的。因此其余线程是没法进入的。
public class User
{
private static object obj = new object();
private static int sin = 0;
public string addtion()
{
lock (obj)
{
sin = sin + 10;
Thread.Sleep(500);
return sin.ToString();
}
}
}注意:1.必须是静态字段。否则的话,每次对象的实例化时都会执行一次object obj = new object() ,那么obj每次都是新实例化的对象,其状态确定是没有上锁的,那样的话就没法起到对任意类型进行锁的操做;2.这里用了objct对象,其实其余对象也是能够的,例如:privatestatic List<User> obj = new List<User>(); 也是能够的,lock的参数其实就是一个变量,当有线程进入的时候,将变量的状态设置为锁,当其余线程到达时,读取到变量的状态若是是锁的,那么就等待,若是不是锁的,那么就进入lock代码块去执行;3.lock的参数其实就是充当变量,当第一个线程到达时候,将其设置为锁状态,当此线程执行完lock代码块中的逻辑后,再将此变量设置成未锁状态!至于上述例子中讨论的不一样状况,就是变量如何取值的问题了。
MSDN上说:
一般,应避免锁定 public 类型,不然实例将超出代码的控制范围。 常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock")违反此准则:
若是实例能够被公共访问,将出现 lock (this) 问题。
若是 MyType 能够被公共访问,将出现 lock (typeof (MyType)) 问题。
因为进程中使用同一字符串的任何其余代码都将共享同一个锁,因此出现 lock("myLock") 问题。
最佳作法是定义 private 对象来锁定, 或 private static 对象变量来保护全部实例所共有的数据。
===分析上述的说法:lock(this)出现的问题指的是:lock(this)只对本实例进行锁,其余实例的话就起不到锁的效果,由于lock(this)是将本实例的状态设置为锁,其余实例到达时,this又是值得本身的实例,而不是上次设置为锁的实例,全部就起不到锁的效果了;lock(typeof(MyType))出现的问题是指:他能够对MyType类型的全部实例进行锁的效,果。由于lock(MyType)是将此对象的状态设置为锁,而全部MyType类的实例化都是一个对象MyType,因此他就能够起到对全部实例的锁的效果。虽然其从效果上达到了要求,可是微软如今建议不要使用 lock(typeof(MyType)),由于锁定类型对象是个很缓慢的过程,而且类中的其余线程、甚至在同一个应用程序域中运行的其余程序均可以访问 该类型对象,所以,它们就有可能代替您锁定类型对象,彻底阻止您的执行,从而致使你本身的代码的挂起(红色字体表示看不懂);lock("myLock")出现的问题指的是:由于string是引用类型,若是多个变量的值为“myLock”,那么若是lock("myLock")以后,就是将“myLock”的状态设置为锁,那么在栈中的变量名都指向在堆中的“myLock”,即:当前都是锁的状态了,若是其中一个变量名为str1,那么存在lock("myLock")的同时也存在lock(str1),将两个lock设置在两个方法中,而且新建n个线程来执行此两个方法,就会出现当有一个线程执行lock("myLock")以后,lock(str1)的代码块也上锁了。
例:lock("myLock")static void Main(string[] args) { Thread[] threads = new Thread[500]; for (int i = 0; i < 50; i=i+2) { threads[i] = new Thread(new ParameterizedThreadStart(fun1)); threads[i+1] = new Thread(new ParameterizedThreadStart(fun2)); threads[i].IsBackground = true; threads[i+1].IsBackground = true; threads[i].Start(null); threads[i+1].Start(null); } Console.ReadKey(); } public static void fun1(object obj) { User u = new User(); Console.WriteLine("fun1: "+u.addtion1()); } public static void fun2(object obj) { User u = new User(); Console.WriteLine("fun2:"+u.addtion2()); }public class User { private static object obj = new object(); private static string str1 = "123"; private static int sin1 = 0; private static int sin2 = 1000; public string addtion1() { lock ("123") { sin1 = sin1 + 10; Thread.Sleep(100); return sin1.ToString() + "-" + DateTime.Now.Ticks.ToString(); } } public string addtion2() { lock (str1) { sin2 = sin2 + 10; Thread.Sleep(100); return sin2.ToString() + "-" + DateTime.Now.Ticks.ToString(); } } } 打上断点,调试看就可看出。当线程在lock ("123")中执行时,即便到了Thread.Sleep(100);也会等待,而后再继续执行,这就说明此时lock (str1)也是锁的状态!这就是问题所在! public class User { private static object obj = new object(); private static string str1 = "12345"; private static int sin1 = 0; private static int sin2 = 1000; public string addtion1() { lock ("123") { sin1 = sin1 + 10; Thread.Sleep(100); return sin1.ToString() + "-" + DateTime.Now.Ticks.ToString(); } } public string addtion2() { lock (str1) { sin2 = sin2 + 10; Thread.Sleep(100); return sin2.ToString() + "-" + DateTime.Now.Ticks.ToString(); } } }
打上断点调试看,当线程在lock ("123")中执行时,当到达Thread.Sleep(100)时,lock (str1)中的代码就会执行,这说明此时lock (str1)的状态是未上锁。实际上两个方法是同时执行的,只不过是由于打上了断电,才形成只能看到一步一步的操做!