现在写程序多线程是不可避免的,而且经常会出现多线程访问共同资源的情况。多线程对共同资源的访问,往往会造成数据的混乱和不可预料的结果,因此一般需要加锁访问进行互斥访问。加锁就需要用到lock关键字。所谓“互斥访问”是指,一段代码或者公共变量,在一个时刻只允许一个线程去访问,其他的线程需要等待,直到改线程处理完毕并通知下一个等待的线程去处理。 lock关键字 lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。msdn如是说。 下面举个简单的例子来说明互斥访问:

void timer1_Tick(object sender, EventArgs e)
{
if (RfidCollection.Count != 0)
{
lock (mylock)
{
decimal temp = 1000;
foreach (var num in RfidCollection.Values)
{
if (num < temp)
{
temp = num;
}
}
foreach (var dic in RfidCollection)
{
if (dic.Value == temp)
{
txtRfid.Text = dic.Key + temp.ToString();
}
}RfidCollection.Clear();
}
}}

上面的代码是一个timer里面的代码,RfidCollection是一个全局变量,同时另一个线程中也调用了RfidCollection,如果没有上面的lock就会报错(另一个调用RfidCollection的地方也需要用mylock加锁),因为可能同时访问RfidCollection变量。按照上面的操作完成后就可以使整个程序不报错。 同时需要特别注意的是mylock变量的声明:private static readonly object mylock = new object(); 为什么mylock必须是object且是静态只读,能不能是其他类型?     1、为什么不能lock值类型 比如lock(1)呢?lock使值类型装箱,每次lock的是装箱后的对象。lock其实是类似编译器的语法糖,因此编译器直接限制住不能lock值类型。退一万步说,就算能编译器允许你lock(1),但是object.ReferenceEquals(1,1)始终返回false(因为每次装箱后都是不同对象),也就是说每次都会判断成未申请互斥锁,这样在同一时间,别的线程照样能够访问里面的代码,达不到同步的效果。同理lock((object)1)也不行。     2、Lock字符串 那么lock("xxx")字符串呢?MSDN上的原话是: 锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。     3、MSDN推荐的Lock对象 通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。 而且lock(this)只对当前对象有效,如果多个对象之间就达不到同步的效果。 而自定义类推荐用私有的只读静态对象,比如: private static readonly object obj = new object(); 为什么要设置成只读的呢?这时因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的对象变了,object.ReferenceEquals必然返回false。 4、lock(typeof(Class)) 与锁定字符串一样,范围太广了。    5特殊问题:lock(this)等的详细解释 在以前编程中遇到lock问题总是使用lock(this)一锁了之,出问题后翻看MSDN突然发现下面几行字:通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:如果实例可以被公共访问,将出现C# lock this问题。如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。

来看看C# lock this问题:如果有一个类Class1,该类有一个方法用lock(this)来实现互斥:
publicvoidMethod2()
{
lock(this)
{
System.Windows.Forms.MessageBox.Show("Method2End");
}
}

如果在同一个Class1的实例中,该Method2能够互斥的执行。但是如果是2个Class1的实例分别来执行Method2,是没有互斥效果的。因为这里的lock,只是对当前的实例对象进行了加锁。 Lock(typeof(MyType))锁定住的对象范围更为广泛,由于一个类的所有实例都只有一个类型对象(该对象是typeof的返回结果),锁定它,就锁定了该对象的所有实例,微软现在建议,不要使用lock(typeof(MyType)),因为锁定类型对象是个很缓慢的过程,并且类中的其他线程、甚至在同一个应用程序域中运行的其他程序都可以访问该类型对象,因此,它们就有可能代替您锁定类型对象,完全阻止您的执行,从而导致你自己的代码的挂起。 锁住一个字符串更为神奇,只要字符串内容相同,就能引起程序挂起。原因是在.NET中,字符串会被暂时存放,如果两个变量的字符串内容相同的话,.NET会把暂存的字符串对象分配给该变量。所以如果有两个地方都在使用lock(“my lock”)的话,它们实际锁住的是同一个对象。到此,微软给出了个lock的建议用法:锁定一个私有的static 成员变量。 .NET在一些集合类中(比如ArrayList,HashTable,Queue,Stack)已经提供了一个供lock使用的对象SyncRoot,用Reflector工具查看了SyncRoot属性的代码,在Array中,该属性只有一句话:return this,这样和lock array的当前实例是一样的。ArrayList中的SyncRoot有所不同

get
{
if(this._syncRoot==null)
{
Interlocked.CompareExchange(refthis._syncRoot,newobject(),null);
}
returnthis._syncRoot;

其中Interlocked类是专门为多个线程共享的变量提供原子操作(如果你想锁定的对象是基本数据类型,那么请使用这个类),CompareExchange方法将当前syncRoot和null做比较,如果相等,就替换成new object(),这样做是为了保证多个线程在使用syncRoot时是线程安全的。集合类中还有一个方法是和同步相关的:Synchronized,该方法返回一个对应的集合类的wrapper类,该类是线程安全的,因为他的大部分方法都用lock来进行了同步处理,比如Add方法:

publicoverridevoidAdd(objectkey,objectvalue)
{
lock(this._table.SyncRoot)
{
this._table.Add(key,value);
}
}

这里要特别注意的是MSDN提到:从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合:

QueuemyCollection=newQueue();
lock(myCollection.SyncRoot){
foreach(ObjectiteminmyCollection){
//Insertyourcodehere.
}
}

最后 注意:应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则: 1)如果实例可以被公共访问,将出现 lock (this) 问题; 2)如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题; 3)由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock("myLock") 问题; 最佳做法是定义 private 对象来锁定, 或 private static 对象变量来保护所有实例所共有的数据。 后面的lock锁定对象解释参考了Lock 解读。

转载于:https://www.cnblogs.com/vsdot/archive/2013/04/08/3263322.html

多线程访问共同的代码或者对象:lock避免出错相关推荐

  1. 当析构函数遇到多线程 ── C++ 中线程安全的对象回调

    陈硕 (giantchen_AT_gmail) 本文 PDF  下载: http://www.cppblog.com/Files/Solstice/dtor_meets_mt.pdf 摘要 编写线程安 ...

  2. python sqlite3 多线程_在python中多线程访问sqlite3数据库

    Python标准库中有sqlite3模块,可见对此数据库的认可.不过,此模块在使用时也有限制,同一个数据库连接,不能在不同线程中共享. import threading import sqlite3 ...

  3. 提高C++性能的编程技术笔记:多线程内存池+测试代码

    为了使多个线程并发地分配和释放内存,必须在分配器方法中添加互斥锁. 全局内存管理器(通过new()和delete()实现)是通用的,因此它的开销也非常大. 因为单线程内存管理器要比多线程内存管理器快的 ...

  4. python多线程爬虫实例-Python3多线程爬虫实例讲解代码

    多线程概述 多线程使得程序内部可以分出多个线程来做多件事情,充分利用CPU空闲时间,提升处理效率.python提供了两个模块来实现多线程thread 和threading ,thread 有一些缺点, ...

  5. Multi-thread--提高C++性能的编程技术笔记:多线程内存池+测试代码

    为了使多个线程并发地分配和释放内存,必须在分配器方法中添加互斥锁. 全局内存管理器(通过new()和delete()实现)是通用的,因此它的开销也非常大. 因为单线程内存管理器要比多线程内存管理器快的 ...

  6. Java多线程学习三十一:ThreadLocal 是用来解决共享资源的多线程访问的问题吗?

    ThreadLocal 是不是用来解决共享资源的多线程访问的. 这是一个常见的面试问题,如果被问到了 ThreadLocal,则有可能在你介绍完它的作用.注意点等内容之后,再问你:ThreadLoca ...

  7. 基于C API的MySQL数据库多线程访问方法

    说明:如何生成线程式客户端 客户端库总是线程安全的.最大的问题在于从套接字读取的net.c中的子程序并不是中断安全的.或许你可能希望用自己的告警中断对服务器的长时间读取,以此来解决问题.如果为SIGP ...

  8. 深入浅出多线程系列之四:简单的同步 lock

    1: 考虑下下面的代码: class ThreadUnsafe     {         static int _val1 = 1, _val2 = 1; internal static void  ...

  9. CoreData和SQLite多线程访问时的线程安全问题

    数据库读取操作一般都是多线程访问的.在对数据进行读取时,我们要保证其当前状态不能被修改,即读取时加锁,否则就会出现数据错误混乱. IOS中常用的两种数据持久化存储方式:CoreData和SQLite, ...

最新文章

  1. 快速排序(快排)--->注释超详细
  2. Grails示例程序-导出Excel文档
  3. Python-快速排序算法
  4. C语言插入排序算法及代码
  5. 退出Activity(转)
  6. 二级域名和二级目录的联系与区别
  7. 虚拟服务器ip是什么意思,虚拟主机独立ip是什么意思
  8. matlab参数摄动仿真,《过程控制工程及仿真:基于MATLAB/Simulink》随书光盘
  9. python接口自动化(二十九)--html测试报告通过邮件发出去——上(详解)
  10. Opatch java 路径_Windows平台下opatch apply报错:OUI-67073
  11. sql sever2005中实现“级联删除”
  12. Naive Bayes text classification
  13. excel如何把顺序倒过来_excel表格怎么把字倒过来
  14. Java switch 使用枚举类
  15. ShowWindow
  16. [机器学习]模型评估方法
  17. 微信跑腿小程序怎么做
  18. 拜托,使用 Three.js 让二维图片具有 3D 效果超酷的
  19. 企业微信封号规则及解封
  20. rabbitmq java 重连_RabbitMQ Java客户端自动重新连接

热门文章

  1. LSH︱python实现MinHash-LSH及MinHash LSH Forest——datasketch(四)
  2. count(1)与count(id)与count(*)效率,以及覆盖索引,索引下推
  3. leetcode题解—1021、删除最外层的括号
  4. 201521123070 《JAVA程序设计》第8周学习总结
  5. 互联网金融爬虫怎么写-第二课 雪球网股票爬虫(正则表达式入门)
  6. 主键,唯一索引,唯一约束三者之间的联系与区别
  7. 2014年值得关注的10个开源项目(上)
  8. Qt显示调用C++的dll
  9. 在C#中调用Java代码
  10. java堆排序图解_108-堆排序的思路图解_清华毕业老程序员亲授通俗易懂的Java数据结构和算法​​​​教程_Java视频-51CTO学院...