C#中的lock关键字有何作用

  作为C#的程序员来说,在遇到线程同步的需求时最常用的就是lock关键字。但如何正确并有效地使用lock,却是能否高效地达到同步要求的关键。正因为如此,程序员需要完全理解lock究竟为程序做了什么。

  所涉及的知识点

• lock的等效代码

• System.Threading.Monitor类型的作用和使用方法

  分析问题

1.lock的等效代码

  在.NET的多线程程序中,经常会遇到lock关键字来控制同步,比如下列代码:

private object o = new object();

public void Work()

{

  lock(o)

  {

    //做一些需要线程同步的工作

  }

}

  事实上,lock这个关键字是C#为方便程序员而定义的语法,它等效于安全地使用System.Threading.Monitor类型。上面的代码就直接等效于下面的代码:

private object o = new object();

public void Work()

{

  //这里很重要,是为了避免直接使用私有成员o,而导致线程不安全

  object temp = o;

  System.Threading.Monitor.Enter(temp);

  try

  {

    //做一些需要线程同步的工作

  }

  finally

  {

    System.Threading.Monitor.Exit(temp);

  }

}

  正如你看到的,真正实现了线程同步功能的,就是System.Threading.Monitor类型,lock关键字只是用来代替调用Enter、Exit方法,并且将所有的工作包含在try块内,以保证其最终退出同步。

2.System.Threading.Monitor类型的作用和使用

  在前文中已经提到了,Monitor类型的Enter和Exit方法用来实现进入和退出对象的同步。具体来说,当Enter方法被调用时,对象的同步索引将被检查,并且.NET将负责一系列的后续工作,来保证对象访问时线程的同步,而Exit方法的调用,则保证了当前线程释放该对象的同步块。

  示例

  下列演示了自定义的类如何利用lock关键字(也就是Monitor类型)来实现线程同步,具体定义了一个包含需要同步执行方法的类型。

/// <summary>

/// 演示同步锁

/// </summary>

public class MyLock

{

//用来在静态方法中同步

private static object o1 = new object();

//用来在成员方法中不同

private object o2 = new object();

//成员变量

private static int i1 = 0;

private int i2 = 0;

/// <summary>

/// 测试静态方法的同步

/// </summary>

/// <param name=" handleObject ">同步时操作的对象</param>

public static void Increment1(object handleObject)

{

lock (o1)

{

Console.WriteLine("i1的值为:{0}", i1);

//这里刻意制造线程并行机会,来检查同步的功能

Thread.Sleep(200);

i1++;

Console.WriteLine("i1自增后为:{0}", i1);

}

}

/// <summary>

/// 测试成员方法的同步

/// </summary>

/// <param name=" handleObject ">同步时操作的对象</param>

public void Increment2(object handleObject)

{

lock (o2)

{

Console.WriteLine("i2的值为:{0}", i2);

//这里刻意制造线程并行机会,来检查同步的功能

Thread.Sleep(200);

i2++;

Console.WriteLine("i2自增后为:{0}", i2);

}

}

}

这样在main方法中,调用该类型对象的方法和其静态方法,测试其同步的效果。代码如下

/// <summary>

/// 程序入口

/// </summary>

class Program

{

  /// <summary>

   /// 测试同步效果

   /// </summary>

  static void Main(string[] args)

  {

  //开始多线程

  Console.WriteLine("开始测试静态方法的同步");

  for (int i = 0; i < 5; i++)

  {

  Task t = new Task(MyLock.Increment1, i);

   t.Start();

  }

  //这里等待线程执行结束

  Thread.Sleep(3 * 1000);

  Console.WriteLine("开始测试成员方法的同步");

  MyLock myLock = new MyLock();

  //开始多线程

  for (int i = 0; i < 5; i++)

  {

  Thread t = new Thread(myLock.Increment2);

   t.Start();

  }

  Console.Read();

  }

}

下面是程序的执行结果:

开始测试静态方法的同步

i1的值为:0

i1自增后为:1

i1的值为:1

i1自增后为:2

i1的值为:2

i1自增后为:3

i1的值为:3

i1自增后为:4

i1的值为:4

i1自增后为:5

开始测试成员方法的同步

i2的值为:0

i2自增后为:1

i2的值为:1

i2自增后为:2

i2的值为:2

i2自增后为:3

i2的值为:3

i2自增后为:4

i2的值为:4

i2自增后为:5

   总结

  可以看到,线程同步被很好地保证了。这里需要强调的是,线程同步本身违反了多线程并行运行的原则,所以读者在使用线程同步时应该尽量做到把lock加在最小的程序块上。如果一个方法有大量的代码需要线程同步,那就需要重新考虑程序的设计了,是否真的有必要进行多线程处理,毕竟线程本身的开销也是相当大的。

  对静态方法的同步,一般采用静态私有的引用成员,而对成员方法的同步,一般采用私有的引用成员。读者需要注意静态和非静态成员的使用,都把同步对象申明为私有,这是保证线程同步高效并且正确的关键点。

说明:以上内容是根据网上内容进行整理。

时间: 2024-11-23 13:09:40

C#中的lock关键字有何作用的相关文章

C# 中使用Lock关键字

 一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待.这是通过在代码块运行期间为给定对象获取互斥锁来实现的. 在多线程中,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数.这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生. 而在.NET中

C#中的lock关键字

前几天与同事激烈讨论了一下,有一点收获,记录起来. 首先给出MSDN的定义: lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.这是通过在代码块运行期间为给定对象获取互斥锁来实现的. 先来看看执行过程,代码示例如下: 假设线程A先执行,线程B稍微慢一点.线程A执行到lock语句,判断obj是否已申请了互斥锁, 判断依据是逐个与已存在的锁进行object.ReferenceEquals比较(此处未加证实),如果不存 在,则申请一个新的互斥锁,这时线程A进入lock里面了. 这时假设

【转】java中关键字volatile的作用

在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器).为了性能,一个线程会在自己的memory中保持要访问的变量的副本.这样就会出现同一个变量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来避免这种情况

java中关键字volatile的作用

用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来避免这种情况的.volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A) =========================分割线1================================= 版权声明 :转载时请以超链接形式标明文章原始出处和作者信息及本

转!!java中关键字volatile的作用

用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来避免这种情况的.volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A) =========================分割线1================================= 版权声明 :转载时请以超链接形式标明文章原始出处和作者信息及本

转:C#中Monitor对象与Lock关键字的区别分析

Monitor对象1.Monitor.Enter(object)方法是获取 锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取锁之后因为异常,致锁 无法释放,所以需要在try{} catch(){}之后的finally{}结构体中释放锁(Monitor.Exit()).2.Monitor的常用属性和方法: Enter(Object) 在指定对象上获取排他锁. Exit(Object) 释放指定对象上的排他锁. IsEnte

c#多线程中Lock()关键字的用法小结

本篇文章主要是对c#多线程中Lock()关键字的用法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助 本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待. 每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数.这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生.

C#中Monitor对象与Lock关键字的区别分析

这篇文章主要介绍了C#中Monitor对象与Lock关键字的区别,需要的朋友可以参考下 Monitor对象 1.Monitor.Enter(object)方法是获取 锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取锁之后因为异常,致锁 无法释放,所以需要在try{} catch(){}之后的finally{}结构体中释放锁(Monitor.Exit()).2.Monitor的常用属性和方法: Enter(Object)

C#中Lock关键字的使用

C# 中的 Lock 语句通过隐式使用 Monitor 来提供同步功能.lock 关键字在块的开始处调用 Enter,而在块的结尾处调用 Exit. 通常,应避免锁定 public 类型,否则实例将超出代码的控制范围. 常见的结构 lock (this).lock (typeof (MyType)) 和 lock ("myLock") 违反此准则: 如果实例可以被公共访问,将出现 lock (this) 问题. 如果 MyType 可以被公共访问,将出现 lock (typeof (M