转自 http://www.cnblogs.com/tianma3798/p/6290158.html

参考官网  https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/lock-statement

一、lock关键词说明

1. lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。

2. lock 语句块锁定,功能等同于

Monitor.Enter(obj);
//代码段
Monitor.Exit(obj);

3. lock语句块锁定和Monitor线程锁,不能跨进程同步

二、备注

lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

线程 这节讨论了线程处理。

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 语句的正文不能使用 等待 关键字。

三、特别说明

1.lock语句中锁定的必须是引用类型的对象,不能是值类型

(1)值类型 一般都在线程函数自己的栈里,每个线程局部栈是不一样的,互相之间不会有影响,所以不用锁定
一个特例,引用类型值类型字段在堆里,但可以通过lock那个引用类型对象就可以实现了。

(2)引用可以指向同一个对象,而值类型的变量每次都是不同的

(3)他要的就是引用类型,如果你传一个值类型,会装箱,下次代码运行到这里,又会装箱,两次不是同一个对象,所以锁不住。这个解释最靠谱

2.为了避免死锁,lock的对象需要是private对象

3.为了避免lock对象的唯一性,通lock的对象为 private static或者 private readonly static

第二章,什么时候需要锁

转自 http://www.cnblogs.com/plin2008/archive/2009/06/24/1510057.html

首先要理解锁定是解决竞争条件的,也就是多个线程同时访问某个资源,造成意想不到的结果。比如,最简单的情况是,一个计数器,两个线程 同时加一,后果就是损失了一个计数,但相当频繁的锁定又可能带来性能上的消耗,还有最可怕的情况死锁。那么什么情况下我们需要使用锁,什么情况下不需要 呢?

1)只有共享资源才需要锁定
      只有可以被多线程访问的共享资源才需要考虑锁定,比如静态变量,再比如某些缓存中的值,而属于线程内部的变量不需要锁定。

2)多使用lock,少用Mutex
      如果你一定要使用锁定,请尽量不要使用内核模块的锁定机制,比如.NET的Mutex,Semaphore,AutoResetEvent和 ManuResetEvent,使用这样的机制涉及到了系统在用户模式和内核模式间的切换,性能差很多,但是他们的优点是可以跨进程同步线程,所以应该清 楚的了解到他们的不同和适用范围。

3)了解你的程序是怎么运行的
      实际上在web开发中大多数逻辑都是在单个线程中展开的,一个请求都会在一个单独的线程中处理,其中的大部分变量都是属于这个线程的,根本没有必要考虑锁定,当然对于ASP.NET中的Application对象中的数据,我们就要考虑加锁了。

4)把锁定交给数据库
      数 据库除了存储数据之外,还有一个重要的用途就是同步,数据库本身用了一套复杂的机制来保证数据的可靠和一致性,这就为我们节省了很多的精力。保证了数据源 头上的同步,我们多数的精力就可以集中在缓存等其他一些资源的同步访问上了。通常,只有涉及到多个线程修改数据库中同一条记录时,我们才考虑加锁。

5)业务逻辑对事务和线程安全的要求
      这 条是最根本的东西,开发完全线程安全的程序是件很费时费力的事情,在电子商务等涉及金融系统的案例中,许多逻辑都必须严格的线程安全,所以我们不得不牺牲 一些性能,和很多的开发时间来做这方面的工作。而一般的应用中,许多情况下虽然程序有竞争的危险,我们还是可以不使用锁定,比如有的时候计数器少一多一, 对结果无伤大雅的情况下,我们就可以不用去管它。

三、小题练练

转自 http://www.fx114.net/qa-276-77022.aspx

看看以下会不会产生死锁:

public class A
{
private object obj = new object();
public void Test(int i)
{
lock (obj)
{
if (i>)
{
i--;
Test(i);
}
else
{
Console.WriteLine(i);
}
}
}
}
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Test();
Console.ReadKey();
}
}

你的答案是会产生死锁吗?可以理解,因为也许你以前一直以为外层递归被锁住后,不允许在访问里边的代码快,而由于递归,有需要等待外层递归解锁,所以由此造成死锁,现在才知道这种理解是错误的;结果是永远不会出现死锁,因为lock的本质是对于不同的线程来说的,或者这个所的所有权已经归该现成所有,它可以任意使用该锁

说道这里又想多说一点,假如另一个线程在另一个函数里操作同一个obj,这是obj可以被访问吗在锁释放之前,如下代码:

public class Test
{
string name = "Empty"; public Test()
{ } public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
}
}
} public class A
{
Test test = new Test(); public void OneMethod()
{
lock (this.test)
{
Console.WriteLine("one thread come in,sleep for 10 seconds");
Console.WriteLine(test.Name);
Thread.Sleep();//如果第二个线程可以在锁住期间可以对test对象操作,那么线程二应该在10000毫秒之内已经输出
test.Name = "One thread output";
Console.WriteLine(test.Name);
}
} public void TwoMethod()
{
Thread.Sleep();//为了确保第一个线程先锁住后在操作test对象
Console.WriteLine("two thread come in");//假如由于线程一锁住test对象而不能对test操作的话,那么要等待10000毫秒
test.Name = "Two thread output";
Console.WriteLine(test.Name);
}
}
class Program
{
static void Main(string[] args)
{
A a = new A();
Thread one = new Thread(new ThreadStart(a.OneMethod));
Thread two = new Thread(new ThreadStart(a.TwoMethod));
one.Start();
two.Start();
Console.ReadKey();
}
} 其输出结果为: one thread come in,sleep for seconds Empty two thread come in//在第一个线程之间操作 Two thread output//在第一个线程锁住test对象之中,改变的test。Name属性值,说明锁中test时将可以对test进行操作 One thread output

答案是:test对象在别的函数中被锁中时,别的线程在被锁块外是可以对test进行操作的

假如我把第二个方法代码做如下修改:

public void TwoMethod()
{
Thread.Sleep();//为了确保第一个线程先锁住后在操作test对象
Console.WriteLine("two thread come in");//假如由于线程一锁住test对象而不能对test操作的话,那么要等待10000毫秒
lock (this.test)
{
test.Name = "Two thread output";
Console.WriteLine(test.Name);
}
} 这时的结果是: one thread come in,sleep for seconds Empty two thread come in//在第一个线程获得锁之间线程二进入了第二方法中的锁之前操作 One thread output// Twothread output//说明锁test被解锁以后才执行

答案是:一个类中几个方法锁住了同一个对象(该对象必须是引用类型)时,那么第一个获得锁的线程操作,其他线程走到锁对象(哪怕所对象不是在同一个函数中)之前将等待对象被解锁,解锁后才可以进入。打个比喻:test对象相当于一个响应器,lock()锁相当于一个锁,操作系统相当于一个监控器,而一个或多个函数中的锁块相当于一个或多大门,多个线程相当于等在一个或多个大门前的人。假如有任一一个大门被一个人闯进时,那么监控器立即给所有锁上锁lock,锁定所有大门,不准任何人进,直到该人出来,监控器自动打开所有锁,所有大门打开,重复执行第一步,监控器保证只有一个人进入某一个大门。当然如果某一个响应器没有lock,就相当于大门没有锁,就可以对test操作,这种情况就是上一种现象。

二、一道面试题(目前我也不太明白)

转自 https://www.cnblogs.com/myshell/archive/2010/07/18/1780386.html

和https://q.cnblogs.com/q/47290/

public void test(int i)
{
lock(this)
{
if (i > )
{
i--;
test(i);
}
}
}

分析一下代码,当调用test方法时i>10时是否会引起死锁

不会。终于在《CLR via C#》第二版(中文版,清华大学出版社出版)的第530页中第7行找到了这样的描述:“同样需要引起注意的是线程可以递归拥有同步块”。即同一线程可以递归调用lock语句,这是单线程操作,该线程本身就是锁的拥有者,当再次进入时,自然能获取到进入该锁块的权利。

线程重入问题,lock会与线程关联,如关联上A1线程,那么A1线程重复进入多次也是OK的。但是lock又是独占锁,所以一旦关联上线程,别的线程就无法进入lock代码块。

多线程操作时,会阻塞等待。

c#Lock学习笔记的更多相关文章

  1. JUC.Condition学习笔记[附详细源码解析]

    目录 Condition的概念 大体实现流程 I.初始化状态 II.await()操作 III.signal()操作 3个主要方法 Condition的数据结构 线程何时阻塞和释放 await()方法 ...

  2. JUC.Lock(锁机制)学习笔记[附详细源码解析]

    锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...

  3. 并发编程学习笔记(4)----jdk5中提供的原子类及Lock使用及原理

    (1)jdk中原子类的使用: jdk5中提供了很多原子类,它会使变量的操作变成原子性的. 原子性:原子性指的是一个操作是不可中断的,即使是在多个线程一起操作的情况下,一个操作一旦开始,就不会被其他线程 ...

  4. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  5. ASP.Net开发基础温故知新学习笔记

    申明:本文是学习2014版ASP.Net视频教程的学习笔记,仅供本人复习之用,也没有发布到博客园首页. 一.一般处理程序基础 (1)表单提交注意点: ①GET通过URL,POST通过报文体: ②需在H ...

  6. linux驱动开发之块设备学习笔记

    我的博客主要用来存放我的学习笔记,如有侵权,请与我练习,我会立刻删除.学习参考:http://www.cnblogs.com/yuanfang/archive/2010/12/24/1916231.h ...

  7. Linux 学习笔记

    Linux学习笔记 请切换web视图查看,表格比较大,方法:视图>>web板式视图 博客园不能粘贴图片吗 http://wenku.baidu.com/view/bda1c3067fd53 ...

  8. SQLite学习笔记(七)&&事务处理

    说到事务一定会提到ACID,所谓事务的原子性,一致性,隔离性和持久性.对于一个数据库而言,通常通过并发控制和故障恢复手段来保证事务在正常和异常情况下的ACID特性.sqlite也不例外,虽然简单,依然 ...

  9. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

随机推荐

  1. 关于学习oi的一些事项

    我只是突然有感而发!(脑抽罢了 我其实是那种一直都没有计划说去学什么的人. 当然也不是那种点开洛谷一道题去写这道题不会就去学习相应的知识点的人. 随着洛谷 poj bzoj HDU CH Vojs 等 ...

  2. 添加图片后xcode报错:resource fork, Finder information, or similar detritus not allowed

    /Users/songximing/Library/Developer/Xcode/DerivedData/Children-cvqgzlzddltkfndhdmzedxbnoxwx/Build/Pr ...

  3. day4_处理json

    说明:#json是一种通用的数据类型,所有的语言都认识.#k - v {}#json串就是一个字符串,不能根据key-value取值#json可以转成字典#json串就是字符串,可放在三引号中校验js ...

  4. 内部排序->基数排序->链式基数排序

    文字描述 基数排序是和前面各类排序方法完全不相同,前面几篇文章介绍的排序算法的实现主要是通过关键字间的比较和移动记录这两种操作,而实现基数排序不需要进行记录关键字间的比较.基数排序是一种借助多关键字排 ...

  5. ajax 上传文件,显示进度条,进度条100%,进度条隐藏,出现卡顿就隐藏进度条,显示正在加载,再显示上传完成

    <form id="uploadForm" method="post" enctype="multipart/form-data"&g ...

  6. jquery中选取兄弟节点的方法

    $('#id').siblings() 当前元素所有的兄弟节点$('#id').prev() 当前元素前一个兄弟节点$('#id').prevaAll() 当前元素之前所有的兄弟节点$('#id'). ...

  7. set的经典应用

    set的经典应用 刚开始用map标记一个测试点超时了 ┭┮﹏┭┮: 用set的find 减少了循环提高了效率 #include <bits/stdc++.h>using namespace ...

  8. window 系统显示svg、psd格式文件

    可以安装SVG Explorer Extension来预览略缩图原地址:https://svgextension.codeplex.com 参考地址 github上 exe 文件下载地址 https: ...

  9. gitlab+jenkins多项目,多依赖,继承等上下级项目关系的自动部署

    案例: 现有三个项目,Common,entity,serviceAPIentity依赖于Common,serviceAPI依赖于entity,也就是说common的下级项目是entity,而entit ...

  10. MySQL的nnodb引擎表数据分区存储

    Symlinks are fully supported only for MyISAM tables. 对应Innodb引擎数据文件放到其他目录 mysql> SHOW VARIABLES L ...