谈谈 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 这是多线程的第二篇. 多线程就像武学中对的吸星大法,理解透了用好了可以得道成仙,俯瞰芸 ...
随机推荐
- 【探索】无形验证码 —— PoW 算力验证
先来思考一个问题:如何写一个能消耗对方时间的程序? 消耗时间还不简单,休眠一下就可以了: Sleep(1000) 这确实消耗了时间,但并没有消耗 CPU.如果对方开了变速齿轮,这瞬间就能完成. 不过要 ...
- C#学习资源
# 视频 C#程序设计 Cousera(推荐) # 文档 C#教程 MSDN Microsoft API 和参考目录
- LeetCode: 3Sum
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all un ...
- Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用
OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...
- OpenCASCADE Expression Interpreter by Flex & Bison
OpenCASCADE Expression Interpreter by Flex & Bison eryar@163.com Abstract. OpenCASCADE provide d ...
- Linux 添加新磁盘,在线扩充空间
CentOS 7开发环境中的home 目录空间满了,需要增加空间 到虚拟机上执行"ls /sys/class/scsi_host",然后重新扫描SCSI总线来添加设备.如右图.然后 ...
- C++ 11 多线程--线程管理
说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...
- 深入理解javascript函数定义与函数作用域
最近在学习javascript的函数,函数是javascript的一等对象,想要学好javascript,就必须深刻理解函数.本人把思路整理成文章,一是为了加深自己函数的理解,二是给读者提供学习的途径 ...
- H3 BPM引擎API接口
引擎API接口通过 Engine 对象进行访问,这个是唯一入口. 示例1:获取组织机构对象 this.Engine.Organization.GetUnit("组织ID"); 示例 ...
- C#事件-使用事件需要的步骤
事件是C#中另一高级概念,使用方法和委托相关.奥运会参加百米的田径运动员听到枪声,比赛立即进行.其中枪声是事件,而运动员比赛就是这个事件发生后的动作.不参加该项比赛的人对枪声没有反应. 从程序员的角度 ...