C# lock 关键字的一些理解
C# lock 关键字的一些理解
问题1:谁是锁?
lock 这个关键字,并不是“锁”,真正的“锁”是那个被lock的Object类型的“对象”,请注意,这里为“对象”加了双引号着重强调被lock的是对象类型。
问题2:这个锁有什么用?
在C# lock关键字定义如下:
lock(expression) statement_block //其中expression代表你希望跟踪的对象,通常是对象引用。
根据lock的定义,它有两种作用
作用1:锁住括号中的对象
只让当前线程拥有该对象的变量、方法、属性等的使用权,lock后一个时刻只可能被一个线程操作。
如果你想保护一个类的实例,一般地,你可以使用this;如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了。
作用2:锁住()后面的代码块(一般放在{}中)
就是定义中的statement_block,这里代表互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。
举个例子,多数商场厕所的蹲位都是小单间型的,也就是一次只能进去一个人,商如何确保每次只能进去一个人呢?不就是一个人进去之后顺手把门锁上么?这样你在里面干啥事,外边的人也只能等待你解放完了,才能进入。以此类推,某个 object 对象被lock之后,lock这个对象的那个线程就拥有了执行lock()后面{}的完整的独立执行权,完整且独立的执行,不可分割的执行,也就是说,{}是一块临界代码段。
看下面代码
private static object objlock = new object();
lock (objlock)
{
//这里要做一些事情
}
根据问题1的答案进行推导,objlock才是那把锁,lock 一下,当前线程就获得了objlock对象与紧跟在lock()后面的{}的独立使用权,在此期间,其他谁都得等着拥有objlock钥匙的线程执行完之后才能使用{}里面的代码。
更专业一点的说法是:在.Net中,每个对象都有一个与之关联的锁,对象可以得到并释放它以便在任意时间只有一个线程可以访问对象实例变量和方法。同样.Net中的每一个对象都可以提供一个允许自己进入等待状态的机制。上面提到的独立使用权,就是对象的互斥锁。(关于这个说法,求证无果,只是从博客中找到的,msdn上暂时没找到相关说法)。
问题3.为什么只能lock引用类型?
因为只有引用类型才有互斥锁,如果强行lock值类型,c#会把值装箱成引用类型,下一次再lock,还会装箱,这两次装箱实际上是装成了两个箱子,就不是用一个内存区间了,所以锁的概念就没有意义。
问题4.避免lock public类型的对象是为什么?
还是以厕所为例子吧,私有就好比,这把锁只有你能访问到,而且最好这把锁不会因为外力而有所改变,别人访问不到,这样才能保证你进去了,别人就进不去了,如果是公有的,就好比你蹲位小单间的锁不是安装在里面而是安装在外边的,别人想不想进就不是你所能控制的了,这样也不安全。
问题5.下面代码中_dic到底能不能被其他线程操作?
private static Dictionary<int, BoKwdCueItem> _dic=new Dictionary<int, BoKwdCueItem>();
static object _obj = new object();
lock (_obj)
{
///对_dic的写操作
}
个人理解上 _dic 存在被其他线程修改的可能。
private static Dictionary<int, BoKwdCueItem> _dic=new Dictionary<int, BoKwdCueItem>();
static object _obj = new object();
lock (_obj)
{
///对_dic的写操作
} public void AddToDic(int data)
{
_dic.Add(data);
}
假设A线程执行到lock(_obj)的{}的一半的时候,B线程获取的CPU的使用权,然后B线程调用了AddToDic方法,就会修改_dic的数据。
以上内容为网上搜集与个人理解,我也不保证一定对。
问题6.为什么msdn建议的是使用lock锁住private static readonly的对象?
private static readonly object obj = new object();
为什么要设置成只读的呢?这时因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的对象变了,object.ReferenceEquals必然返回false。
至于private,上面已经提到了。
问题7.lock究竟是什么?
lock关键字其实就是封装了Moniter类的一些操作,详情介绍见msdn上关于lock的解释,下面是原文摘录。
lock 语句具有以下格式
lock (x)
{
// Your code...
}
其中 x 是引用类型的表达式。 它完全等同于
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
关于Monitor类这里暂时不做过多的解释。
倒是msdn上的一个示例非常值得借鉴,应该算是lock最简单的应用方式了。以下示例定义了一个 Account 类,该类通过锁定专用的 balanceLock 实例来同步对其专用 balance 字段的访问。 使用相同的实例进行锁定可确保尝试同时调用 Debit 或 Credit 方法的两个线程无法同时更新 balance 字段。
using System;
using System.Threading.Tasks; public class Account
{
private readonly object balanceLock = new object();
private decimal balance; public Account(decimal initialBalance)
{
balance = initialBalance;
} public decimal Debit(decimal amount)
{
lock (balanceLock)
{
if (balance >= amount)
{
Console.WriteLine($"Balance before debit :{balance, 5}");
Console.WriteLine($"Amount to remove :{amount, 5}");
balance = balance - amount;
Console.WriteLine($"Balance after debit :{balance, 5}");
return amount;
}
else
{
return ;
}
}
} public void Credit(decimal amount)
{
lock (balanceLock)
{
Console.WriteLine($"Balance before credit:{balance, 5}");
Console.WriteLine($"Amount to add :{amount, 5}");
balance = balance + amount;
Console.WriteLine($"Balance after credit :{balance, 5}");
}
}
} class AccountTest
{
static void Main()
{
var account = new Account();
var tasks = new Task[];
for (int i = ; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() => RandomlyUpdate(account));
}
Task.WaitAll(tasks);
} static void RandomlyUpdate(Account account)
{
var rnd = new Random();
for (int i = ; i < ; i++)
{
var amount = rnd.Next(, );
bool doCredit = rnd.NextDouble() < 0.5;
if (doCredit)
{
account.Credit(amount);
}
else
{
account.Debit(amount);
}
}
}
}
我想着重说明的一些部分:
a、balanceLock的用法与命名。balanceLock在逻辑上是人为绑定给balance用的“锁”。
b、考虑这样一个问题:如果上面代码再加两个方法直接在不lock balanceLock对象的情况下操作balance,那么这两个方法在异步操作的时候,会出现什么情况??如果一个带lock的方法和一个不带lock的方法对balance异步操作又会出现什么情况??
答案在本文中找。
后记:好多重复,逻辑也有点乱,不打算改了,以上顺序就是在思考lock关键字的时候就是不断出现的问题的顺序,留作思路。
参考文章:
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/lock-statement
https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.monitor?redirectedfrom=MSDN&view=netframework-4.7.2
http://www.cnblogs.com/promise-7/articles/2354077.html
C# lock 关键字的一些理解的更多相关文章
- lock关键字的使用
最近在代码中,发现别人使用了lock关键字,为了理解别人写的代码,所以自己对lock关键字的使用研究了下. 微软官方解释,请百度:lock 语句(C# 参考) 微软给了个官网实例代码: class A ...
- lock关键字理解
>可以把lock关键字可以看成 try{ Monitor.Enter(x); //.. } finally{ Monitor.Exit(x); } 这样子的结构,当然使用lock关键字更方便 & ...
- C#中的lock关键字有何作用
作为C#的程序员来说,在遇到线程同步的需求时最常用的就是lock关键字.但如何正确并有效地使用lock,却是能否高效地达到同步要求的关键.正因为如此,程序员需要完全理解lock究竟为程序做了什么. 所 ...
- 锁·——lock关键字详解
作 者:刘铁猛 日 期:2005-12-25 关键字:lock 多线程 同步 小序 锁者,lock关键字也.市面上的书虽然多,但仔细介绍这个keyword的书太少了.MSDN里有,但所给的代码非常 ...
- 《C#多线程编程实战》1.10 lock关键字
lock关键字是锁定资源用的. 书上的代码解释很好. /// <summary> /// 抽象类 加减法 /// </summary> abstract class Count ...
- 带你轻松了解C# Lock 关键字
相信绝大多数.NET玩家和我一样,常常使用Timer这个对象,而在WPF中使用DispatcherTimer的人也是很多,DispatcherTimer是在UI线程跑的.我们的程序中大多数都会充斥很多 ...
- lock关键字只不过是C#提供的语法糖
lock关键字只不过是C#提供的语法糖, 最终使用的还是Monitor类. Monitor类的Enter方法要求传入的参数不为null, 否则会有ArgumentNullException excep ...
- C#的lock关键字
using System; using System.Threading; namespace Test { class Program { //一.Lock定义 //lock 关键字可以用来确保代码 ...
- C# 使用lock关键字lock不同的对象
c# lock关键字的本质 是调用Monitor.Enter(object obj)并且在finally的时候调用Monitor.Exit(obj) 在obj是不同数据类型的时候会出现不同的情况 1. ...
随机推荐
- centos下jdk、jre安装
1.在/usr/目录下创建java目录 [root@localhost ~]# mkdir/usr/java [root@localhost ~]# cd /usr/java 2.下载jdk,然后解压 ...
- HDU1007.Quoit Design
-- 点我 -- 题目大意 :给你一堆点,求一个最小圆能够覆盖两个点的半径(最近两点距离的一半): 最多100000个点,暴力即O(n^2)会超时,考虑二分,先求左边最短距离dl,右边dr, 和一个点 ...
- JDBC操作数据库步骤
2018-11-04 20:23:24开始写 1.加载驱动程序(Class.forName) 2.建立连接获取数据库连接对象(DriverManager.getConnection) 3.向数据库发 ...
- QPushButton 控制两种状态
[1]Custom.cpp #include "CustomButton.h" CustomButton::CustomButton(QWidget* parent) : QPus ...
- 仿照admin的stark自定义组件的功能实现
仿照admin的stark自定义组件的功能实现:其中最主要的就是增删改查的实现 1.查:首先页面中显示表头和数据,都是动态的,而不是写死的. (1) 先看表头和表单数据:这个是查看的视图函数,但是为了 ...
- 开发vue单页面Demo
第1步:安装webpack脚手架 npm install webpack -g (全局安装) (新电脑启动npm run dev版本报错,是因为webpack-server版本更新的问题,要安装pac ...
- 【Alpha版本】冲刺阶段——Day3
[Alpha版本]冲刺阶段--Day3 阅读目录 今日进展 问题困难 明日任务 今日贡献量 TODOlist [今日进展] 密码算法方面: 参考了md5/sha1+salt和Bcrypt后,我们决定使 ...
- webpack4.0 实战记录
从零配置webpack4.0 搭建React工程. 基本环境:Node(v8.1.2)+ webpack(v4.16.2) 1.在项目目录 命令窗口 执行 npm init 初始化项目,执行完后项 ...
- Linux服务器---邮件服务openwebmail配置
配置openwebmail 通过修改配置文件openwebmail.conf,实现个性化邮箱定制.用户可以自由的更改邮箱logo.域名.容量 .签名等信息. 1.重定向index,快速打开邮箱.将下面 ...
- php ci nginx 伪静态rewrite配置方法
php ci nginx 伪静态rewrite配置方法 location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=/$ ...