(); public void Work() { lock (locker) { // 做一些需要线程同步的工作 } }
事实上,lock关键字时一个方便程序员使用的语法糖,它等效于安全地使用System.Threading.Monitor类型,它直接等效于下面的代码:
(); public void Work() { temp = locker; Monitor.Enter(temp); try { // 做一些需要线程同步的工作 } finally { Monitor.Exit(temp); } }
(2)System.Threading.Monitor类型的作用和使用
Monitor类型的Enter和Exit方法用来实现进入和退出对象的同步,当Enter方法被调用时,对象的同步索引将被检查,并且.NET将负责一系列的后续工作来保证对象访问时的线程同步,而Exit方法的调用则保证了当前线程释放该对象的同步块。
下面的代码示例演示了如何使用lock关键字来实现线程同步:
class Program { static void Main(string[] args) { // 多线程测试静态方法的同步 Console.WriteLine(); for (int i = 0; i < 5; i++) { Thread thread = new Thread(Lock.StaticIncrement); thread.Start(); } // 这里等待线程执行结束 Thread.Sleep(5 * 1000); Console.WriteLine(); // 多线程测试实例方法的同步 Console.WriteLine(); Lock l = new Lock(); for (int i = 0; i < 6; i++) { Thread thread = new Thread(l.InstanceIncrement); thread.Start(); } Console.ReadKey(); } } public class Lock { staticLocker = new object(); instanceLocker = new object(); staticNumber = 0; private int instanceNumber = 0; StaticIncrement(object state) { lock (staticLocker) { Console.WriteLine(, Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine(, staticNumber.ToString()); // 这里可以制造线程并行执行的机会,来检查同步的功能 Thread.Sleep(200); staticNumber++; Console.WriteLine(, staticNumber.ToString()); } } InstanceIncrement(object state) { lock (instanceLocker) { Console.WriteLine(,Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine(, instanceNumber.ToString()); // 这里可以制造线程并行执行的机会,来检查同步的功能 Thread.Sleep(200); instanceNumber++; Console.WriteLine(, instanceNumber.ToString()); } } }
View Code下图是该实例的执行结果:
PS:线程同步本身违反了多线程并行运行的原则,所以我们在使用线程同步时应该尽量做到将lock加在最小的程序块上。对于静态方法的同步,一般采用静态私有的引用对象成员,而对于实例方法的同步,一般采用私有的引用对象成员。
3.3 可否使用值类型对象来实现线程同步吗?前面已经说到,在.NET中每个堆内的对象都会有一个同步索引字段,用以指向同步块的位置。但是,对于值类型来说,它们的对象是分配在堆栈上的,也就是说值类型是没有同步索引这一字段的,所以直接使用值类型对象无法实现线程同步。
如果在程序中对于lock关键字使用了值类型对象,会直接导致一个编译错误:
3.4 可否使用引用类型对象自身进行同步?