谈谈 Lock
上来先看MSDN关于lock的叙述:
lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。 下面的示例包含一个 lock 语句。
lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
线程处理(C# 和 Visual Basic) 这节讨论了线程处理。
lock 关键字在块的开始处调用 Enter,而在块的结尾处调用 Exit。 ThreadInterruptedException 引发,如果 Interrupt 中断等待输入 lock 语句的线程。
通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。 常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:
• 如果实例可以被公共访问,将出现 lock (this) 问题。
• 如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。
• 由于进程中使用同一字符串的任何其他代码都将共享同一个锁,所以出现 lock("myLock") 问题。
最佳做法是定义 private 对象来锁定, 或 private static 对象变量来保护所有实例所共有的数据。
在 lock 语句的正文不能使用 等待 关键字。
通读上面叙述后,再来几个事例看看:
例一:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; namespace NB.Demo
{
class Program
{
static void Main(string[] args)
{
Test t = new Test();
Thread[] threads = new Thread[];
for (int i = ; i < threads.Length; i++)
{ threads[i] = new Thread(() =>
{
t.Print();
}); threads[i].Name = "thread" + i; } for (int i = ; i < threads.Length; i++)
{
threads[i].Start();
} Console.Read();
}
}
class MyLock
{
public int Id { get; set; }
} class Test
{
public void Print()
{
MyLock myLock = new MyLock();
lock (myLock)
{
for (int i = ; i < ; i++)
{
Console.WriteLine("\t" + Thread.CurrentThread.Name.ToString() + "\t" + i.ToString() + " ");
}
}
}
}
}

输出结果: 线程出现了争抢,这不是我们想要的结果,我们预期是每次只有一个线程去执行Print方法。
例二:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; namespace NB.Demo
{
class Program
{
static void Main(string[] args)
{
Test t = new Test();
Test t0 = new Test();
Thread[] threads = new Thread[];
for (int i = ; i < threads.Length; i++)
{ threads[i] = new Thread(() =>
{
t.Print();
t0.Print();
}); threads[i].Name = "thread" + i; } for (int i = ; i < threads.Length; i++)
{
threads[i].Start();
} Console.Read();
}
}
class MyLock
{
public int Id { get; set; }
} class Test
{
public void Print()
{
lock (this)
{ for (int i = ; i < ; i++)
{
Console.WriteLine("\t" + Thread.CurrentThread.Name.ToString() + "\t" + i.ToString() + " ");
}
}
}
}
}

输出结果: 同样线程出现了争抢,这不是我们想要的结果。
例三:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; namespace NB.Demo
{
class Program
{
static void Main(string[] args)
{
Test t = new Test(); Thread[] threads = new Thread[];
for (int i = ; i < threads.Length; i++)
{ threads[i] = new Thread(() =>
{
t.Print();
}); threads[i].Name = "thread" + i; } for (int i = ; i < threads.Length; i++)
{
threads[i].Start();
} Console.Read();
}
}
class MyLock
{
public int Id { get; set; }
} class Test
{
MyLock myobj = new MyLock();
public void Print()
{
lock (myobj)
{ for (int i = ; i < ; i++)
{
Console.WriteLine("\t" + Thread.CurrentThread.Name.ToString() + "\t" + i.ToString() + " ");
}
}
}
}
}

例四:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; namespace NB.Demo
{
class Program
{
static void Main(string[] args)
{
Test t = new Test(); Thread[] threads = new Thread[];
for (int i = ; i < threads.Length; i++)
{ threads[i] = new Thread(() =>
{
t.Print();
}); threads[i].Name = "thread" + i; } for (int i = ; i < threads.Length; i++)
{
threads[i].Start();
} Console.Read();
}
}
class MyLock
{
public int Id { get; set; }
} class Test
{
private static object obj = new object();
public void Print()
{
lock (obj)
{ for (int i = ; i < ; i++)
{
Console.WriteLine("\t" + Thread.CurrentThread.Name.ToString() + "\t" + i.ToString() + " ");
}
}
}
}
}

例五:其他情况 如 Lock(type), Lock("string"),Lock("this")等
总结:
引用类型对象除实例字段的开销外,还有两个字段的开销:类型指针和同步块索引(SyncBlockIndex)。 在.net CLR世界,lock实际上是Moniter的包装。CLR初始化的时候,CLR会初始化一个SyncBlock的数组,当一个线程到达Monitor.Enter方法时,该线程会检查该方法接受的参数的同步块索引,默认情况下对象的同步块索引是一个默认值,那么表明该对象并没有一个关联的同步块,CLR就会在全局的SyncBlock数组里找到一个空闲的项,然后将数组的索引赋值给该对象的同步块索引,SyncBlock的内容和CRITICAL_SECTION的内容很相似,当Monitor.Enter执行时,它会设置SyncBlock里的内容,标识出已经有一个线程占用了,当另外一个线程进入时,它就会检查SyncBlock的内容,发现已经有一个线程占用了,该线程就会等待,当Monitor.Exit执行时,占用的线程就会释放SyncBlock,其他的线程可以进入操作了。
针对例一,锁定的对象是作为一个局部变量,每个线程进入的时候,锁定的对象都会不一样,它的SyncBlock每一次都是重新分配的,这个根本谈不上什么锁定不锁定。
针对例二,锁定的对象是Test类事例this,Test t和Test t0所代表的this都会不一样,它的SyncBlock就不一样,所以这种Lock也是无效的。
针对例五的那些其他情况:
一般说来应该没有什么事情,但这些操作却是很危险的。
typeof(..)得到的是..类的Type对象,所有..实例的Type都是同一个,Type对象也是一个对象,它也有自己的SyncBlock,Singleton的Type对象的SyncBlock在程序中只会有一份,为什么说这种做法是危险的呢?如果在该程序中,其他毫不相干的地方我们也使用了lock(typeof(..)),虽然它和这里的锁定毫无关系,但是只要一个地方锁定了,各个地方的线程都会在等待。
string也是应用类型,从语法上来说是没有错的。但是锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。
针对例三,本试验是没问题,但这种方式是不是有点不好的地方,留给各位思考。
谈谈 Lock的更多相关文章
- C#中lock死锁实例教程
这篇文章主要介绍了C#中lock死锁的用法,对于共享资源的访问及C#程序设计的安全性而言,有着非常重要的意义!需要的朋友可以参考下 链接:http://www.jb51.net/article/543 ...
- 谈谈线程同步Lock和unLock
Lock可以使用Condition进行线程之间的调度,它有更好的灵活性,而且在一个对象里面可以有多个Condition(即对象监视器),则线程可以注册在不同的Condition,从而可以 有选择性的调 ...
- 谈谈如何使用Netty开发实现高性能的RPC服务器
RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...
- 从一个简单的Java单例示例谈谈并发
一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这么写 public class UnsafeLazyInitiallization { private static Un ...
- 从一个简单的Java单例示例谈谈并发 JMM JUC
原文: http://www.open-open.com/lib/view/open1462871898428.html 一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这 ...
- 《深入浅出 Java Concurrency》—锁紧机构(一)Lock与ReentrantLock
转会:http://www.blogjava.net/xylz/archive/2010/07/05/325274.html 前面的章节主要谈谈原子操作,至于与原子操作一些相关的问题或者说陷阱就放到最 ...
- 谈谈java的BlockingQueue
http://www.cnblogs.com/archy_yu/archive/2013/04/19/3018479.html 博客园 首页 新随笔 联系 管理 随笔- 92 文章- 0 评论- ...
- BAT美团滴滴java面试大纲(带答案版)之三:多线程Lock
继续面试大纲系列文章. 这是多线程的第二篇. 多线程就像武学中对的吸星大法,理解透了用好了可以得道成仙,俯瞰芸芸众生:而滥用则会遭其反噬. 在多线程编程中要渡的第二个“劫”,则是Lock.在很多时候, ...
- BAT美团滴滴java面试大纲(带答案版)之四:多线程Lock
每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 这是多线程的第二篇. 多线程就像武学中对的吸星大法,理解透了用好了可以得道成仙,俯瞰芸 ...
随机推荐
- JavaScript权威指南 - 函数
函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...
- 理解CSS外边距margin
前面的话 margin是盒模型几个属性中一个非常特殊的属性.简单举几个例子:只有margin不显示当前元素背景,只有margin可以设置为负值,margin和宽高支持auto,以及margin具有 ...
- 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...
- 【资源】.Net 入门@提高 - 逆天的高薪之路!
入门看视频,提高看书籍,飘升做项目.老练研开源,高手读外文,大牛讲低调~ 官方学习计划 http://www.cnblogs.com/dunitian/p/5667901.html ----- ...
- 【SQLServer】【恢复挂起的解决方案】附加文件时候的提示“无法重新生成日志,原因是数据库关闭时存在打开的事务/用户,该数据库没有检查点或者该数据库是只读的。 ”【数据库恢复】
汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 先贴错误: 吐槽一下: 进入正题: 新建一个同名数据库 停止MSSQL服务 替换数据库文 ...
- 【.net 深呼吸】启动一个进程并实时获取状态信息
地球人和火星人都知道,Process类既可以获取正在运行的进程,也可以启动一个新的进程.在79.77%应用场合,我们只需要让目标进程顺利启动就完事了,至于它执行了啥,有没有出错,啥时候退出就不管了. ...
- AFNetworking 3.0 源码解读(八)之 AFImageDownloader
AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...
- interpreter(解释器模式)
一.引子 其实没有什么好的例子引入解释器模式,因为它描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发编译器中:在实际应用中,我们可能很少碰到去构造一个语言的文法的情况. 虽然你几乎用 ...
- 【JQ基础】数组
each() 方法规定为每个匹配元素规定运行的函数.
- DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记
今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...