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加在最小的程序块上。如果一个方法有大量的代码需要线程同步,那就需要重新考虑程序的设计了,是否真的有必要进行多线程处理,毕竟线程本身的开销也是相当大的。
对静态方法的同步,一般采用静态私有的引用成员,而对成员方法的同步,一般采用私有的引用成员。读者需要注意静态和非静态成员的使用,都把同步对象申明为私有,这是保证线程同步高效并且正确的关键点。
说明:以上内容是根据网上内容进行整理。
C#中的lock关键字有何作用的更多相关文章
- C#中的lock关键字;就是lock住一个大家都共同访问的(静态static的)东东就行了
public class ChatService : IChat //继承IChat接口或者说IChat的实现类 { //定义一个静态对象用于线程部份代码块的锁定,用于lock操作 private s ...
- C#中的lock关键字
前几天与同事激烈讨论了一下,有一点收获,记录起来. 首先给出MSDN的定义: lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.这是通过在代码块运行期间为给定对象获取互斥锁来实现的. ...
- C#中的lock关键字(初识)
http://kb.cnblogs.com/page/88513/ 首先给出MSDN的定义: lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.这是通过在代码块运行期间为给定对象获取互 ...
- (转载)C#中的lock关键字
lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.这是通过在代码块运行期间为给定对象获取互斥锁来实现的. 先来看看执行过程,代码示例如下: 假设线程A先执行,线程B稍微慢一点.线程A执 ...
- java中的this关键字三种作用
1.表示类中的属性和调用方法2.调用本类中的构造方法3.表示当前对象
- java中关键字volatile的作用
用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...
- C#中Monitor类、Lock关键字和Mutex类
线程:线程是进程的独立执行单元,每一个进程都有一个主线程,除了主线程可以包含其他的线程.多线程的意义:多线程有助于改善程序的总体响应性,提高CPU的效率.多线程的应用程序域是相当不稳定的,因为多个线程 ...
- 转!!java中关键字volatile的作用
用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...
- C++中关键字explicit的作用
C++中, 一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色. 1 是个构造器 ,2 是个默认且隐含的类型转换操作符. 所以, 有时候在我们写下如 AAA ...
随机推荐
- 1.C#中几个简单的内置Attribute
阅读目录 一:Obsolete 二:Conditional 一:Obsolete 这个内置属性是说这个方法废弃了不可用,它有两个参数,第一个参数message是说废弃的原因,第二个参数err ...
- Aspose转PDF时乱码问题的解决
主要原因是服务器上一般安装的字体都是有限的,而我们日常生活工作中总是喜欢用一些比较特别的字体,比如宋体GB2312,这时候如果用Aspose转PDF就会出现乱码,解决方法也比较简单,把本地的特殊字体拷 ...
- 《objective-c基础教程》学习笔记(九)—— Foundation框架介绍
在之前的博文中,我们创建的项目文件的时候,默认都有引用#import <Foundation/foundation.h> 这个头文件.但是,之前我们对Foundation都没有展开介绍.这 ...
- 实现 Dispose 方法
实现 Dispose 方法 MSDN 类型的 Dispose 方法应释放它拥有的所有资源.它还应该通过调用其父类型的 Dispose 方法释放其基类型拥有的所有资源.该父类型的 Dispose 方法应 ...
- js实现表单序列化的两种方法。
function serialize(form) { var parts = [], elems = form.elements, i = , len = elems.length, filed = ...
- Ubuntu14.04安装中文输入法以及解决Gedit中文乱码问题
1 设置中文显示环境 1. 打开System Settings 2. 打开Personal-> Language Support. 会弹出如下对话框,提示你“语言支持没安装完整”. 点击“Rem ...
- Win8.1 Metro应用无法联网,提示“无法加载此页面”解决方法!(看红色字体部分)
Windows 8 Metro Apps使用Proxifier的方法 By Shinichi_Wtn2012-07-05 20:28 在校园里,经常要使用代理上网的方式,Proxifier可以让不支持 ...
- Qt Style Sheets Examples——定制前景色和背景色
例子取自:http://qt-project.org/doc/qt-4.8/stylesheet-examples.html 以lineEdit为例 (1)设置某个lineEdit的背景色为黄色 li ...
- Qt snippet — 打开文件&保存文件
打开文件: void Notepad::on_actionOpen_triggered() { QString fileName = QFileDialog::getOpenFileName(this ...
- Debian 基本设置
在此做一下记录,省得以后再老是去搜索了 我的debian版本v:8.1.0 我是在Hyper-V中装的,启动后一直ping不通网址,后来才知道Debian不能直接ping域名,只能ping IP... ...