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 关键字的一些理解的更多相关文章

  1. lock关键字的使用

    最近在代码中,发现别人使用了lock关键字,为了理解别人写的代码,所以自己对lock关键字的使用研究了下. 微软官方解释,请百度:lock 语句(C# 参考) 微软给了个官网实例代码: class A ...

  2. lock关键字理解

    >可以把lock关键字可以看成 try{ Monitor.Enter(x); //.. } finally{ Monitor.Exit(x); } 这样子的结构,当然使用lock关键字更方便 & ...

  3. C#中的lock关键字有何作用

    作为C#的程序员来说,在遇到线程同步的需求时最常用的就是lock关键字.但如何正确并有效地使用lock,却是能否高效地达到同步要求的关键.正因为如此,程序员需要完全理解lock究竟为程序做了什么. 所 ...

  4. 锁·——lock关键字详解

    作  者:刘铁猛 日  期:2005-12-25 关键字:lock 多线程 同步 小序 锁者,lock关键字也.市面上的书虽然多,但仔细介绍这个keyword的书太少了.MSDN里有,但所给的代码非常 ...

  5. 《C#多线程编程实战》1.10 lock关键字

    lock关键字是锁定资源用的. 书上的代码解释很好. /// <summary> /// 抽象类 加减法 /// </summary> abstract class Count ...

  6. 带你轻松了解C# Lock 关键字

    相信绝大多数.NET玩家和我一样,常常使用Timer这个对象,而在WPF中使用DispatcherTimer的人也是很多,DispatcherTimer是在UI线程跑的.我们的程序中大多数都会充斥很多 ...

  7. lock关键字只不过是C#提供的语法糖

    lock关键字只不过是C#提供的语法糖, 最终使用的还是Monitor类. Monitor类的Enter方法要求传入的参数不为null, 否则会有ArgumentNullException excep ...

  8. C#的lock关键字

    using System; using System.Threading; namespace Test { class Program { //一.Lock定义 //lock 关键字可以用来确保代码 ...

  9. C# 使用lock关键字lock不同的对象

    c# lock关键字的本质 是调用Monitor.Enter(object obj)并且在finally的时候调用Monitor.Exit(obj) 在obj是不同数据类型的时候会出现不同的情况 1. ...

随机推荐

  1. GE与POST方法区别

    1.用途. GET方法一般用于查询并获取信息,这意味着它是幂等的(对同一个url的多个请求,返回结果完全一样),因为没有修改资源状态,所以它是安全的.而POST一般用于更新资源信息,既不是幂等,也不是 ...

  2. Android开发随笔记_1

    1):android:configChanges="keyboardHidden|orientation":配置的好处:一般在AndroidManifest.xml文件中都没有使用 ...

  3. Yii2 数据缓存/片段缓存/页面缓存/Http缓存

  4. poj2114 寻找树上存在长度为k点对,树上的分治

    寻找树上存在长度为k点对,树上的分治  代码和  这个  差不多 ,改一下判断的就好 #include <iostream> #include <algorithm> #inc ...

  5. MyBatis学习笔记(一)——MyBatis快速入门

    转自孤傲苍狼的博客:http://www.cnblogs.com/xdp-gacl/p/4261895.html 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优 ...

  6. 大数据是什么?它和Hadoop又有什么联系?

    随着近几年计算机技术和互联网的发展,“大数据”这个名词越来越多进入我们的视野.大数据的快速发展也在无时无刻影响着我们的生活. 那大数据究竟是什么呢? 首先,看看专家是怎么解释大数据的: 大数据就是多, ...

  7. flask模板应用-加载静态文件:添加Favicon,使用CSS框架,使用宏加载静态资源

    加载静态文件 一个Web项目不仅需要HTML模板,还需要许多静态文件,比如CSS.JavaScript文件.图片和声音声.在flask程序中,默认需要将静态文件存储在与主脚本(包含程序实例的脚本)同级 ...

  8. PCB 布线,直角线,差分线,蛇形线

    1.直角线 直角走线的一般标准是PCB布线中要尽量避免的情况,也几乎成为衡量布线好坏的标准之一. 直角走线对信号的影响主要体系那在下面三个方面 1.保教可以等效为传输线是哪个的容性负载,减缓上升时间. ...

  9. ActiveMQ任意文件写入漏洞(版本在5.12.X前CVE-2016-3088)

    ActiveMQ任意文件写入漏洞(版本在5.12.X前CVE-2016-3088) 查看docker的activemq版本命令:$ docker ps | grep activemq927860512 ...

  10. 【JavaScript 6连载】五、继承的概念

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...