一. 为什么要lock,lock了什么?

当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待。但当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当对同一个资源进行读写的时候,我们要使该资源在同一时刻只能被一个线程操作,以确保每个操作都是有效即时的,也即保证其操作的原子性。lock是C#中最常用的同步方式,格式为lock(objectA){codeB} 。

lock(objectA){codeB} 看似简单,实际上有三个意思,这对于适当地使用它至关重要:
1. objectA被lock了吗?没有则由我来lock,否则一直等待,直至objectA被释放。
2. lock以后在执行codeB的期间其他线程不能调用codeB,也不能使用objectA。
3. 执行完codeB之后释放objectA,并且codeB可以被其他线程访问。

二. lock(this)怎么了?

我们看一个例子:

class C1
{
private bool deadlocked = true;//这个方法用到了lock,我们希望lock的代码在同一时刻只能由一个线程访问
public void LockMe(object o)
{
lock (locker)
{
while (deadlocked)
{
deadlocked = (bool)o;
Console.WriteLine("Foo: I am locked :(");
Thread.Sleep();
}
}
} //所有线程都可以同时访问的方法
public void DoNotLockMe()
{
Console.WriteLine("I am not locked :)");
}
}
class Program
{
static void Main(string[] args)
{
C1 c1 = new C1();
//在t1线程中调用LockMe,并将deadlock设为true(将出现死锁)
Thread t1 = new Thread(c1.LockMe);
t1.Start(true);
Thread.Sleep();
//在主线程中lock c1
lock (c1)
{
//调用没有被lock的方法
c1.DoNotLockMe();
//调用被lock的方法,并试图将deadlock解除
c1.LockMe(false);
}
}
}

  程序运行结果如上图所示。在t1线程中,LockMe调用了lock(this), 也就是Main函数中的c1,这时候在主线程中调用lock(c1)时,必须要等待t1中的lock块执行完毕之后才能访问c1,即所有c1相关的操作都无法完成,于是我们看到连c1.DoNotLockMe()都没有执行。

  让我们把C1的代码稍作改动,mian()主函数里面的代码保持不变:

class C1
{
private bool deadlocked = true;
private object locker = new object();
//这个方法用到了lock,我们希望lock的代码在同一时刻只能由一个线程访问
public void LockMe(object o)
{
lock (locker)
{
while (deadlocked)
{
deadlocked = (bool)o;
Console.WriteLine("Foo: I am locked :(");
Thread.Sleep();
}
}
} //所有线程都可以同时访问的方法
public void DoNotLockMe()
{
Console.WriteLine("I am not locked :)");
}
}

  程序运行结果如上图所示。这次我们使用一个私有成员作为锁定变量(locker),在LockMe中仅仅锁定这个私有locker,而不是整个对象。这时候重新运行程序,可以看到虽然t1出现了死锁,DoNotLockMe()仍然可以由主线程访问;LockMe()依然不能访问,原因是其中锁定的locker还没有被t1释放。

关键点:
  1. lock(this)的缺点就是在一个线程(例如本例的t1)通过执行该类的某个使用"lock(this)"的方法(例如本例的LockMe())锁定某对象之后, 导致整个对象无法被其他线程(例如本例的主线程)访问 - 因为很多人在其他线程(例如本例的主线程)中使用该类的时候会使用类似lock(c1)的代码。
  2. 锁定的不仅仅是lock段里的代码,锁本身也是线程安全的。
  3. 我们应该使用不影响其他操作的私有对象作为locker。
  4. 在使用lock的时候,被lock的对象(locker)一定要是引用类型的,如果是值类型,将导致每次lock的时候都会将该对象装箱为一个新的引用对象(事实上如果使用值类型,C#编译器(3.5.30729.1)在编译时就会给出一个错误)。

C#的值类型包括:结构体(数值类型,bool型,用户定义的结构体),枚举,可空类型。C#的引用类型包括:数组,用户定义的类、接口、委托,object,字符串。

因此,如果有以下定义:
class A
{
}
struct S
{
}
int i;
object o;
string str;
A a=new A();
S s=new S();
则有下面的语法正确或者错误说明,敬请注意。
lock(i){}//错误
lock(o){}//正确
lock(str){}//正确
lock(a){}//正确
lock(s){}//错误

  Lock语句根本使用的就是Monitor.Enter和Monitor.Exit,也就是说lock(this)时执行Monitor.Enter(this),大括号结束时执行Monitor.Exit(this).他的意义在于什么呢,对于任何一个对象来说,他在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,他指向CLR中的SyncBlock Cache区域中的一个SyncBlock.什么意思呢?就是说,当你执行Monitor.Enter(Object)时,如果object的索引值为负数,就从SyncBlock Cache中选区一个SyncBlock,将其地址放在object的索引中。这样就完成了以object为标志的锁定,其他的线程想再次进行Monitor.Enter(object)操作,将获得object为正数的索引,然后就等待。直到索引变为负数,即线程使用Monitor.Exit(object)将索引变为负数。

system.threading.monitor.enter(x);
try
{
...
}
finally
{
system.threading.monitor.exit(x);
}

  某些系统类提供专门用于锁定的成员。例如,Array 类型提供 SyncRoot。许多集合类型也提供 SyncRoot。而自定义类推荐用私有的只读静态对象,比如:

  private static readonly object obj = new object();

  为什么要设置成只读的呢?这时因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的对象变了,object.ReferenceEquals必然返回false。

其它参考连接:

http://www.jb51.net/article/57220.htm

http://www.2cto.com/kf/201110/108043.html

http://www.csharpwin.com/csharpspace/12362r6119.shtml

理解C#的Lock语法意义的更多相关文章

  1. 第十三节:实际开发中使用最多的监视锁Monitor、lock语法糖的扩展、混合锁的使用(ManualResetEvent、SemaphoreSlim、ReaderWriterLockSlim)

    一. 监视锁(Monitor和lock) 1. Monitor类,限定线程个数的一把锁,两个核心方法: Enter:锁住某个资源. Exit:退出某一个资源. 测试案例:开启5个线程同时对一个变量进行 ...

  2. 读书笔记 effective c++ Item 42 理解typename的两种意义

    1. class和typename意义相同的例子 问题:在下面的模板声明中class和typename的区别是什么? template<class T> class Widget; // ...

  3. C#线程中LOCK的意义

    学习心得,为的是让新人能理解,高手直接绕~ lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区.如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放. 引用一句 ...

  4. C# ASP.NET B/S模式下,采用lock语法 实现多用户并发产生不重复递增单号的一种解决方法技术参考

    有时候也好奇,若是老外发个技术文章,会不会到处是有人骂街的?进行人身攻击的?中国人喜欢打击别人,不知道老外是不是也是这个性格?好奇的问一下大家. 往往我们在开发程序.调试程序时,无法模拟多用户同时操作 ...

  5. 理解Global interpreter lock

      Global interpreter lock (GIL) is a mechanism used in computer language interpreters to synchronize ...

  6. C# lock 语法糖实现原理--《.NET Core 底层入门》之自旋锁,互斥锁,混合锁,读写锁

    在多线程环境中,多个线程可能会同时访问同一个资源,为了避免访问发生冲突,可以根据访问的复杂程度采取不同的措施 原子操作适用于简单的单个操作,无锁算法适用于相对简单的一连串操作,而线程锁适用于复杂的一连 ...

  7. [C#基础]说说lock到底锁谁?

    写在前面 最近一个月一直在弄文件传输组件,其中用到多线程的技术,但有的地方确实需要只能有一个线程来操作,如何才能保证只有一个线程呢?首先想到的就是锁的概念,最近在我们项目组中听的最多的也是锁谁,如何锁 ...

  8. C# 说说lock到底锁谁?(1)

    写在前面 最近一个月一直在弄文件传输组件,其中用到多线程的技术,但有的地方确实需要只能有一个线程来操作,如何才能保证只有一个线程呢?首先想到的就是锁的概念,最近在我们项目组中听的最多的也是锁谁,如何锁 ...

  9. 说说lock到底锁谁(I)?

    写在前面 最近一个月一直在弄文件传输组件,其中用到多线程的技术,但有的地方确实需要只能有一个线程来操作,如何才能保证只有一个线程呢?首先想到的就是锁的概念,最近在我们项目组中听的最多的也是锁谁,如何锁 ...

随机推荐

  1. 山东省第四届acm解题报告(部分)

    Rescue The PrincessCrawling in process... Crawling failed   Description Several days ago, a beast ca ...

  2. 安装mysql出现no compatible servers were found

    一.问题描述 今天在安装数据库的过程中,遇到错误提示: No compatible servers were found,You'll need to cancel this wizard and i ...

  3. 在Linux上实现SVN用户密码自助修改

    1.首先本文是在一篇百度经验的基础上修改而成,将网址记录上,以示尊重. https://jingyan.baidu.com/article/48b37f8dd4bbb31a646488c2.html ...

  4. 安装apr-1.6.3报错[cannot remove `libtoolT’: No such file or directory]解决方法

    发现有这个提示:cannot remove `libtoolT’: No such file or directory , 编辑 configure文件,查找 $RM "$cfgfile&q ...

  5. PeopleSoft面试题(服务器相关)

    如何配置app服务器与web服务器的负载均衡?请详细说明. App Server: 在配置App Server负载均衡时候,通过webserv目录下的configuration.properties文 ...

  6. SQLServer 远程链接MySql数据库详解

    SQLServer 远程链接MySql数据库详解 by:授客 QQ:1033553122 测试环境: Microsoft Windows XP Professional 版本2000 Service ...

  7. 大数据【七】HBase部署

    接着前面的Zookeeper部署之后,现在可以学习HBase了. HBase是基于Hadoop的开源分布式数据库,它以Google的BigTable为原型,设计并实现了具有高可靠性.高性能.列存储.可 ...

  8. springboot 学习之路 15(集成shiro)

    shiro: Apache Shiro 是 Java 的一个安全框架.功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案.   更多shiro介 ...

  9. org.postgresql.util.PSQLException: 栏位索引超过许可范围:3,栏位数:2。

    org.postgresql.util.PSQLException: 栏位索引超过许可范围:3,栏位数:2. 今天在写完SQL进行查询的时候,后台一直报错显示上面的信息.看错误完全不知道原因,就重新检 ...

  10. SQL删除多列语句

    最近在写SQL过程中发现需要对一张表结构作调整(此处是SQL Server),其中需要删除多列,由于之前都是一条SQL语句删除一列,于是猜想是否可以一条语句同时删除多列,如果可以,怎么写法? 第一次猜 ...