作为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. android中自定义view---实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色

    android自定义view,实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色. 由于时间比较仓促,因此没有对代码进行过多的优化,功能远远不如androi ...

  2. Hadoop - Zeppelin 使用心得

    1.概述 在编写 Flink,Spark,Hive 等相关作业时,要是能快速的将我们所编写的作业能可视化在我们面前,是件让人兴奋的时,如果能带上趋势功能就更好了.今天,给大家介绍这么一款工具.它就能满 ...

  3. exerunexplorer.exe

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...

  4. Xcode工程使用CocoaPods管理第三方库新建工程时出现错误

    工程使用CocoaPods管理第三方库,在新的目录update版本的时候出现如下问题   问题1描述: diff: /../Podfile.lock: No such file or director ...

  5. C#中控件数组的讨论

    VB用得习惯后,到C#中来觉得很奇怪,如此好的控件数组怎么不见了.“众所周知,控件数组最主要的两个优点:可以循环附值:可以响应同一个事件.从而大大简化了代码.引自http://wenku.baidu. ...

  6. [转]LINQ之路系列博客导航

    分享一个学习Linq的好博客:Linq之路

  7. [PaPaPa][需求说明书][V0.1]

    PaPaPa软件需求说明书V0.1 前   言 我觉得我们废话不能多,废话一多的话大家就找不到重点了,其实本项目是出于技术研究的目的来开发的,大家讨论了半天决定要做社(yue)交(pao)类的项目. ...

  8. iOS开发之应用内检测手机锁屏,解锁状态

    iPhone的锁屏监测分为两种方式监听: 1. 程序在前台,这种比较简单.直接使用Darwin层的通知就可以了: #import <notify.h> #define Notificati ...

  9. mysql 学习碎片

    1.mysql 中执行 sql字符串 set @strSql='select 1200 as item'; prepare select_sql from @strSql; execute selec ...

  10. MyEclipse 2015优化技巧

    MyEclipse 2015优化速度方案仍然主要有这么几个方面:去除无需加载的模块.取消冗余的配置.去除不必要的检查.关闭更新. 第一步: 去除不需要加载的模块 一个系统20%的功能往往能够满足80% ...