作为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加在最小的程序块上。如果一个方法有大量的代码需要线程同步,那就需要重新考虑程序的设计了,是否真的有必要进行多线程处理,毕竟线程本身的开销也是相当大的。

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

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

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

  1. C#中的lock关键字;就是lock住一个大家都共同访问的(静态static的)东东就行了

    public class ChatService : IChat //继承IChat接口或者说IChat的实现类 { //定义一个静态对象用于线程部份代码块的锁定,用于lock操作 private s ...

  2. C#中的lock关键字

    前几天与同事激烈讨论了一下,有一点收获,记录起来. 首先给出MSDN的定义: lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.这是通过在代码块运行期间为给定对象获取互斥锁来实现的. ...

  3. C#中的lock关键字(初识)

    http://kb.cnblogs.com/page/88513/ 首先给出MSDN的定义: lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.这是通过在代码块运行期间为给定对象获取互 ...

  4. (转载)C#中的lock关键字

    lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.这是通过在代码块运行期间为给定对象获取互斥锁来实现的. 先来看看执行过程,代码示例如下: 假设线程A先执行,线程B稍微慢一点.线程A执 ...

  5. java中的this关键字三种作用

    1.表示类中的属性和调用方法2.调用本类中的构造方法3.表示当前对象

  6. java中关键字volatile的作用

    用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...

  7. C#中Monitor类、Lock关键字和Mutex类

    线程:线程是进程的独立执行单元,每一个进程都有一个主线程,除了主线程可以包含其他的线程.多线程的意义:多线程有助于改善程序的总体响应性,提高CPU的效率.多线程的应用程序域是相当不稳定的,因为多个线程 ...

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

    用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...

  9. C++中关键字explicit的作用

    C++中, 一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色. 1 是个构造器 ,2 是个默认且隐含的类型转换操作符. 所以, 有时候在我们写下如 AAA ...

随机推荐

  1. CSS3学习笔记——伪类hover

    最近看到一篇文章:“Transition.Transform和Animation使用简介及应用展示”    ,想看看里面 “不同缓动类效果demo”例子的效果,发现了一个问题如下: .Trans_Bo ...

  2. tldr 的安卓客户端

    上次在 Cheat (tldr, bropages) - Unix命令用法备忘单 这篇博文中提到过 tldr ,它跟 cheatsheet 的功能一样:用来查询一些常用命令的惯用法,呈现形式是简明扼要 ...

  3. tensorflow 运行成功!

    折腾了一天安装tensorflow环境,终于可以运行,也记录一下安装中容易犯的错误总结(写给python小白们) 一.win7 双系统 安装ubuntu 16.04 ,参考 http://jingya ...

  4. [原创]Android Studio的Instant Run(即时安装)原理分析和源码浅析

    Android Studio升级到2.0之后,新增了Instant Run功能,该功能可以热替换apk中的部分代码,大幅提高测试安装的效率. 但是,由于我的项目中自定义了一些ClassLoader,当 ...

  5. Window中调试HBase问题小结

    1.好久没用log4j了,转到logback好多年了,hbase程序运行时,报缺少log4j配置,那么,就转去logback吧(以下的XXX表示版本号). 原先lib包里面有log4j-XXX.jar ...

  6. WinForm 批量设置指定控件中的控件状态

    在开发中常遇到当点击某个按钮的时候,禁用文本框或按钮的的状态,以防止误操作,下面的代码是我已批量设置指定控件中的按钮状态的代码,同理可以延伸出很多操作. /// <summary> /// ...

  7. WPF打印票据

    最近工作的内容是有关于WPF的,整体开发没有什么难度,主要是在打印上因为没有任何经验,犯了一些难,不过还好,解决起来也不是很费劲. WPF打印票据或者是打印普通纸张区别不大,只是说打印票据要把需要打的 ...

  8. 结合使用saiku、mondrian workbentch建立多维查询报表

    1.简介 前几篇博客已经介绍了saiku.mondrian.MDX和模式文件他们之间的关系,那么如何将它们串联起来,供产品人员使用哪?下面咱们一步一步的实现 2.建立数据表 建表语句参考:http:/ ...

  9. 《Microsoft SQL Server 2008 Internals》读书笔记

    http://www.cnblogs.com/downmoon/archive/2010/01/26/1656411.html

  10. 几种图片下载lib对比