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 ...
随机推荐
- android实现自动升级并安装打开
http://blog.csdn.net/wa991830558/article/details/41014673 这是一个比较简单的程序,但网上还是有很多人问起这个问题,并且回答的人,也没有完全回答 ...
- LESS详解之函数(四)
之前已经为大家介绍了一些LESS函数,大家应该对之前介绍的有所了解了.下面依旧为大家介绍LESS的函数,附加着一些小例子.希望这些有关LESS的函数能在大家编写LESS的时候有所帮助. saturat ...
- linux系统和依赖包常用下载地址
http://ftp.gnome.org/pub/gnome/sources/gstreamer/0.10/ http://www.linuxfromscratch.org/blfs/view/svn ...
- Embeding Python & Extending Python with FFPython
Introduction ffpython is a C++ lib, which is to simplify tasks that embed Python and extend Python. ...
- js 判断微信浏览器
上周接到个需求,需求是这样的:用户扫一扫二维码会产生一个链接,该链接会向后端发送个请求,返回一个 apk 的下载地址,用户点击下载按钮可以下载此 apk.然后就发生了问题,经过测试,发现用微信扫一扫打 ...
- pentaho saiku 安装全过程
公司希望也开发一套多维分析系统,以解决运营/产品无休止的需求和技术人力不足的矛盾! 一.开发选型: 一.BIRT:易用性差,所以没再使用 二.JasperReport+ireport:文档收费,不支持 ...
- 如何让js不产生冲突,避免全局变量的泛滥,合理运用命名空间
为了避免变量之间的覆盖与冲突,可以生成命名空间,命名空间是一种特殊的前缀,在js中,通过{ }对象实现. 在不同的匿名函数中,根据功能声明一个不同的命名空间,每个匿名函数中GLOBAL对象的属性都不直 ...
- 【NS2仿真】RTP协议安装
来自: http://personales.upv.es/fboronat/Research/NS2_RTP/NS2_RTP_RTCP_module.htm 文件:http://pan.baidu.c ...
- 如何交换两个等长整形数组使其数组和的差最小(C和java实现)
1. 问题描述: 有两个数组a,b,大小都为n,数组元素的值任意整形数,无序: 要求:通过交换a,b中的元素,使[数组a元素的和]与[数组b元素的和]之间的差最小. 2. 求解思路: 当前数组a和数组 ...
- android自定义之 5.0 风格progressBar
最近做项目,用到了ProgressBar ,就想到了要使用Android5.0 的效果,就随手实现了一下. 效果图: 大概的思路: 1. 圆圈通过Canvas去绘制 2.圆圈的动画通过Animator ...