初步学习lock的见解
在应用程序中使用多个线程的一个好处是每个线程都可以异步执行。对于 Windows 应用程序,耗时的任务可以在后台执行,而使应用程序窗口和控件保持响应。对于服务器应用程序,多线程处理提供了用不同线程处理每个传入请求的能力。否则,在完全满足前一个请求之前,将无法处理每个新请求。然而,线程的异步特性意味着必须协调对资源(如文件句柄、网络连接和内存)的访问。否则,两个或更多的线程可能在同一时间访问相同的资源,而每个线程都不知道其他线程的操作。结果将产生不可预知的数据损坏。lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。 lock 语句以关键字 lock 开头,它有一个作为参数的对象,在该参数的后面还有一个一次只能由一个线程执行的代码块。
{
privateint i =0;
publicvoid FirstThread()
{
Thread one =new Thread(ThreadOne);
Thread two =new Thread(ThreadTwo);
one.Start();
two.Start();
//Console.ReadLine();
}
publicvoid ThreadOne()
{
lock (this)
{
Console.WriteLine(i);
Console.WriteLine("First");
Thread.Sleep(1000);
Console.WriteLine(i);//this中的属性依然能够被别的线程改变. i = 1
Console.WriteLine("ThreadOne");
}
}
publicvoid ThreadTwo()
{
Thread.Sleep(500);
this.i =1;
Console.WriteLine(i);
Console.WriteLine("Change the value in locking");
}
}
class LockTwo
{
privateint i =0;
publicvoid SecondThread()
{
Thread one =new Thread(ThreadOne);
Thread two =new Thread(ThreadTwo);
one.Start();
two.Start();
Console.ReadLine();
}
publicvoid ThreadOne()
{
lock (this)
{
Console.WriteLine(i);
Console.WriteLine("Second");
Thread.Sleep(1000);
Console.WriteLine(i);
Console.WriteLine("SecondThreadOne");
}
}
publicvoid ThreadTwo()
{
lock (this) //较前面的ThreadTwo()方法多了个lock(this)
{
Thread.Sleep(500);
this.i =1;
Console.WriteLine(i);
Console.WriteLine("Can't change the value in locking");
}
}
}
staticvoid Main(string[] args)
{
LockOne lockOne =new LockOne();
lockOne.FirstThread(); //在多线程中不能用顺序来考虑的 并不是当该方法结束后才执行下一个方法
LockTwo lockTwo =new LockTwo();
lockTwo.SecondThread();
}
/*0
* First //该线程sleep1000
* 0
* Second //该线程sleep1000
* 1 // 该线程sleep500
* Change the value in locking
* 1
* ThreadOne
* 0
* SecondThreadOne
* 1
* Can't change the value in locking
*/
本想在案例一中lock住this对象,让其他的线程不能操作,可是事情不是像我们想象的那样,this中的属性依然能够被别的线程改变.那我们lock住的是什么?是代码段,是lock后面大括号中代码段,这段代码让多个人执行是不被允许的(当ThreadTwo中没有添加lock时,ThreadOne仍可以访问two线程中的ThreadTwo()).那返回头来在看lock(this),this是什么意思呢?可以说this只是这段代码域的标志,ThreadTwo中的lock需要等到ThreadOne种lock释放后才开始运行,释放之前一直处于等待状态,这就是标志的表现.
提供给 lock 关键字的参数必须为基于引用类型的对象,该对象用来定义锁的范围。在上例中,锁的范围限定为此函数,因为函数外不存在任何对该对象的引用。严格地说,提供给 lock 的对象只是用来唯一地标识由多个线程共享的资源,所以它可以是任意类实例。然而,实际上,此对象通常表示需要进行线程同步的资源。例如,如果一个容器对象将被多个线程使用,则可以将该容器传递给 lock,而 lock 后面的同步代码块将访问该容器。只要其他线程在访问该容器前先锁定该容器,则对该对象的访问将是安全同步的。
lock这段代码是怎么运行的.lock语句根本使用的就是Moniter.Enter和Moniter.Exit,也就是说lock(this)时执行Monitor.Enter(this),大括号结束时执行Monitor.Exit(this).他的意义在于什么呢,对于任何一个对象来说,他在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,他指向CLR中的SyncBlock Cache区域中的一个SyncBlock.就是说,当你执行Monitor.Enter(Object)时,如果object的索引值为负数,就从SyncBlock Cache中选区一个SyncBlock,将其地址放在object的索引中。这样就完成了以object为标志的锁定,其他的线程想再次进行Monitor.Enter(object)操作,将获得object为正数的索引,然后就等待。直到索引变为负数,即线程使用Monitor.Exit(object)将索引变为负数。lock后括号里面的值不是说把整个对象锁住,而是对他的一个值进行了修改,使别的lock不能锁住他,这才是lock(object)的真面目.在静态方法中如何使用lock呢,由于我们没有this可用,所以我们使用typeof(this)好了,Type也有相应的方法地址和索引,所以他也是可以来当作lock的标志的.但微软不提倡使用public的object或者typeof()或者字符串这样的标志,因为如果你的public object在其他的线程中被null并被垃圾收集了,将发生不可预期的错误. 微软给出了个lock的建议用法:锁定一个私有的static 成员变量。
通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象,即整个程序中还存在其他地方也锁定了该实例。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。因此,最好锁定不会被暂留的私有或受保护成员。某些类提供专门用于锁定的成员。例如,Array 类型提供 SyncRoot。许多集合类型也提供 SyncRoot。
转自城市兔子的线程,同步与锁————Lock你到底锁住了谁