lock学习篇(上)
why?
当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待。
但当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当对同一个资源进行读写的时候,
我们要使该资源在同一时刻只能被一个线程操作,以确保每个操作都是有效即时的,也即保证其操作的原子性。
lock是C#中最常用的同步方式,格式为lock(objectA){codeB} 。
lock (objectA) { codeB}
看似简单,实际上有三个意思,这对于适当地使用它至关重要:
1.objectA被lock了吗?没有则由我来lock,否则一直等待,直至objectA被释放。
2.lock以后在执行codeB的期间其他线程不能调用codeB,也不能使用objectA。
3.执行完codeB之后释放objectA,并且codeB可以被其他线程访问。
https://www.cnblogs.com/apsnet/archive/2012/07/08/2581475.html
即为了保证资源的一致性
when?
需要该资源在同一时刻只能被一个线程操作
how?
通过加锁来确保在操作完成之前不会被第二个资源进行访问
场景一:
存在生产与售出方法
两个方法同时操作库存
code:
// 生产数量
public int MakeCount { get; set; }
// 售出数量
public int SellCount { get; set; }
//库存
private int Products { get; set; }
//产品制造
public void Make(int num)
{
    Products += num;
    MakeCount += num;
    Console.WriteLine($"生产了{num}个{ProductName},当前产品总数为:{Products}");
}
//产品售出
public void Sell(int num)
{
  if (Products < num)
  {
    Console.WriteLine($"库存不足,当前库存为:{Products}");
    return;
  }
  Products -= num;
  SellCount += num;
  Console.WriteLine($"售出了{num}个{ProductName},当前产品总数为:{Products}");
}
result:
  总生产数量: 11,总售出数量: 8,当前库存: 3,实际库存: 3
  总生产数量: 31,总售出数量: 29,当前库存: 1,实际库存: 2
  总生产数量: 51,总售出数量: 47,当前库存: 1,实际库存: 4
  总生产数量: 73,总售出数量: 67,当前库存: 3,实际库存: 6
  总生产数量: 93,总售出数量: 71,当前库存: 19,实际库存: 22
  总生产数量: 97,总售出数量: 71,当前库存: 23,实际库存: 26
解决方法: 在操作数据时加锁,保证在同一时间仅有一个线程操作此数据
private int _products;
public int Products
{
  get
  {
    return this._products;
  }
  set
  {
    lock (this)
      this._products = value;
  }
}
note1: 单方法操作,不会导致数据异常
note2: 当执行时间越短,触发数据异常机遇越小
lock是如何执行的?
lock <==> Monitor
即
lock (this)
{
  _products = value;
}
//等价于:
bool lockTaken = false;
try
{
  Monitor.Enter(this, ref lockTaken);
  _products = value;
}
finally
{
  if (lockTaken) Monitor.Exit(this);
}
https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.monitor?view=netframework-4.8
什么是Monitor?/Monitord有什么作用?
提供同步访问对象的机制。
Monitor的使用
code:
//库存
private int goods { get; set; }
//生产量
private int make { get; set; }
//销售量
private int sell { get; set; }
public void Make()
{
  if (Monitor.TryEnter(_lock, TimeSpan.FromMilliseconds(1000)))
  {
    try
    {
      goods++;
      make++;
      Console.WriteLine($"制造了一个产品,当前产品数量:{goods}");
      ////System.Threading.SynchronizationLockException:“Object synchronization method was called from an unsynchronized block of code.”
      //为避免生产过量,生产完后会提醒销售售出
      Monitor.Pulse(_lock);
    }
    finally
    {
      Monitor.Exit(_lock);
    }
  }
  else
  {
    Console.WriteLine("生成失败");
  }
}
public void Sell()
{
  if (Monitor.TryEnter(_lock, TimeSpan.FromMilliseconds(1000)))
  {
    while (goods <= 0) //库存不足
    {
      //System.Threading.SynchronizationLockException:“Object synchronization method was called from an unsynchronized block of code.”
      //同步方法不能在非同方方法中调用
      if (!Monitor.Wait(_lock, TimeSpan.FromMilliseconds(1000))) //等待生成
      {
        Console.WriteLine("库存不足!");
        return;
      }
    }
    sell++;
    goods--;
    Console.WriteLine($"售出了一个产品,当前库存:{goods}");
  }
  else
  {
    Console.WriteLine($"购买失败");
  }
}
public void Show()
{
  Console.WriteLine($"当前库存:{goods},生产量:{make},销售量:{sell},实际库存:{sell - make}");
  if (make - sell != goods) throw new Exception("销售异常!");
}
note
- 同步索引块是.NET中解决对象同步问题的基本机制
- 这个对象肯定要是引用类型,值类型可不可呢?值类型可以装箱啊!你觉得可不可以?但也不要用值类型,因为值类型多次装箱后的对象是不同的,会导致无法锁定;
- 不要锁定this,尽量使用一个没有意义的Object对象来锁;
- 不要锁定一个类型对象,因类型对象是全局的;
- 不要锁定一个字符串,因为字符串可能被驻留,不同字符对象可能指向同一个字符串;
- 不要使用[System.Runtime.CompilerServices.MethodImpl(MethodImplOptions.Synchronized)],这个可以使用在方法上面,保证方法同一时刻只能被一个线程调用。她实质上是使用lock的,如果是实例方法,会锁定this,如果是静态方法,则会锁定类型对象;
confirm
通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。
常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:
如果实例可以被公共访问,将出现 lock (this) 问题。
如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。
由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。
ext
https://www.cnblogs.com/anding/p/5301754.html
https://www.cnblogs.com/carsonzhu/p/7446953.html
author:monster
since:5/7/2019 2:02:24 PM
direction:lock
lock学习篇(上)的更多相关文章
- c++学习笔记之封装篇(上)
		title: c++学习笔记之封装篇(上) date: 2017-03-12 18:59:01 tags: [c++,c,封装,类] categories: [学习,程序员,c/c++] --- 一. ... 
- 6-C++远征之封装篇[上]-学习笔记
		C++远征之封装篇(上) 课程简介 类(抽象概念),对象(真实具体) 配角: 数据成员和成员函数(构成了精彩而完整的类) 构造函数 & 析构函数(描述了对象的生生死死) 对象复制和对象赋值 ( ... 
- js学习篇1--数组
		javascript的数组可以包含各种类型的数据. 1. 数组的长度 ,直接用 length 属性; var arr=[1,2,3]; arr.length; js中,直接给数组的length赋值是会 ... 
- Tomcat集群配置学习篇-----分布式应用
		Tomcat集群配置学习篇-----分布式应用 现目前基于javaWeb开发的应用系统已经比比皆是,尤其是电子商务网站,要想网站发展壮大,那么必然就得能够承受住庞大的网站访问量:大家知道如果服务器访问 ... 
- (转载)OC学习篇之---概述
		前言 终于开启了OC的学习篇了,之前由于工作上的事,学习就一直搁浅了,不过最近由于各种原因,感觉必须要开启iOS的开发旅程了,不然就老了.因为之前一直是做Android的,所以学习iOS来就没那么费劲 ... 
- zookeeper学习(上)
		zookeeper学习(上) 在前面的文章里我多次提到zookeeper对于分布式系统开发的重要性,因此对zookeeper的学习是非常必要的.本篇博文主要是讲解zookeeper的安装和zookee ... 
- 鸟哥Linux私房菜基础学习篇学习笔记3
		鸟哥Linux私房菜基础学习篇学习笔记3 第十二章 正则表达式与文件格式化处理: 正则表达式(Regular Expression) 是通过一些特殊字符的排列,用以查找.删除.替换一行或多行文字字符: ... 
- 鸟哥Linux私房菜基础学习篇学习笔记2
		鸟哥Linux私房菜基础学习篇学习笔记2 第九章 文件与文件系统的压缩打包: Linux下的扩展名没有什么特殊的意义,仅为了方便记忆. 压缩文件的扩展名一般为: *.tar, *.tar.gz, *. ... 
- 鸟哥Linux私房菜基础学习篇学习笔记1
		鸟哥Linux私房菜基础学习篇学习笔记1 第三章 主导分区(MBR),当系统在开机的时候会主动去读取这个区块的内容,必须对硬盘进行分区,这样硬盘才能被有效地使用. 所谓的分区只是针对64Bytes的分 ... 
随机推荐
- C语言:fopen函数
			在C语言中,操作文件之前必须先打开文件:所谓"打开文件",就是让程序和文件建立连接的过程.打开文件之后,程序可以得到文件的相关信息,例如大小.类型.权限.创建者.更新时间等.在后续 ... 
- 高校表白App-团队冲刺第二天
			今天要做什么 今天要把昨天的activity进行完善,并且加上计时跳转的功能,将其设置为主页面,设置两种跳转功能. 遇到的问题 今天没遇到什么大的问题,只是在进行编写的时候,又出现了R文件无法找到的情 ... 
- 一个很多人不知道的SpringBoot小技能!!
			大家好,我是冰河~~ 最近,发现很多小伙伴在修改了SpringBoot的配置文件后,都要重新编译整个项目,极大的浪费了开发时间.我身边就有很多小伙伴一直是这样做的.那么,有没有什么方式能够修改配置文件 ... 
- 部署springboot时出现的问题
			一.打包出现问题 后经发现是因为maven的打包插件的版本问题,需要修改版本 <plugins> <plugin> <groupId>org.springframe ... 
- Tree Widget  -- 基本方法
			Tree Widget这个空间类似于一种表格的形式,是一种树状结构 效果图: 第一步:打开designer.exe,拖动一个Tree Widget空间到主窗口上 第二步:双击Tree Widget,添 ... 
- Oracle 分页查询的一个实例
			1.分页模板 select * from ( select rownum as rn , a.* from( 某个表名) a) where rn between 0 and 6 2 某个表名 sele ... 
- Samba 远程命令执行漏洞(CVE-2017-7494)
			该漏洞影响Samba 3.5.0之后的所有版本,在4.6.4/4.5.10/4.4.14修复了这个漏洞 use exploit/linux/samba/is_known_pipename set rh ... 
- Nexus Repository Manager 3 远程命令执行漏洞(CVE-2020-10199、CVE-2020-10204)
			[影响版本] Nexus Repository Manager OSS/Pro 3.x <= 3.21.1 poc地址 https://github.com/magicming200/CVE-2 ... 
- Linux的链接(入门)
			Linux的链接分为两种:硬链接和软链接 硬链接:如果B是A的硬链接,那么B和A指向同一个文件,但是删除A并不会影响B->允许一个文件有多个路径 软链接:类似Windows下的快捷方式,删除原文 ... 
- appium自动化测试(4)部分方法&unitest初步使用
			捕捉弹窗 https://github.com/appium/appium/issues/968完整有截屏的例子:https://github.com/bitbar/testdroid-samples ... 
