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. 四 js Math数学简单使用

    //Math是全局的 //Math.PI 数学里的3.1415926.... console.log(Math.PI); //取随机数 //js提供的随机函数 Math.random() --> ...

  2. KindEditor echarts

    var editor; KindEditor.ready(function (K) { editor = K.create('textarea[name="content"]', ...

  3. io 流概念

    io 流概念 对输入输出抽象的封装

  4. 关于JS的几个基础题目

    1.截取字符串abcdefg的efg alert("abcdefg".substring(4)); 2.判断一个字符串中出现次数最多的字符,统计这个次数 var str = 'as ...

  5. ETL面试题集锦

    1. What is a logical data mapping and what does it mean to the ETL team? 什么是逻辑数据映射?它对ETL项目组的作用是什么? 答 ...

  6. c# 规范用户输入控件

    MaskedTextBox控件是一种特殊的文本框,它可以通过Mask属性设置格式标记符.在应用程序运行后,用户只能输入Mask属性允许的内容,列入日期.电话等 在“输入掩码”对话框的右下角有一个“使用 ...

  7. HDU 2064 汉诺塔III (递推)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2064 约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到 ...

  8. [转载]ViewPort <meta>标记

    ViewPort <meta>标记用于指定用户是否可以缩放Web页面,如果可以,那么缩放到的最大和最小缩放比例是什么.使用 ViewPort <meta>标记还表示文档针对移动 ...

  9. Ford VCM II Ford VCM2 Diagnostic Tool with Ford IDS v108 Installed On Laptop Ready to Use

    HOW to VCM2 Ford VCM II with Ford IDS v108 Work Well? VCM2 Ford VCM2 Ford diagnostic tool hot sale i ...

  10. Java中高级面试必问之多线程TOP50(含答案)

    以下为大家整理了今年一线大厂面试被问频率较高的多线程面试题,由于本人的见识局限性,所以可能不是很全面,也欢迎大家在后面留言补充,谢谢. 1.什么是线程? 2.什么是线程安全和线程不安全? 3.什么是自 ...