《C#多线程编程实战》1.10 lock关键字
lock关键字是锁定资源用的。
书上的代码解释很好。
/// <summary>
/// 抽象类 加减法
/// </summary>
abstract class CounterBase
{
/// <summary>
///抽象 加法 方法
/// </summary>
public abstract void Increment(); /// <summary>
/// 抽象 减法 方法
/// </summary>
public abstract void Decrement();
}
/// <summary>
/// 不使用lock关键字的实现抽象类Counter
/// </summary>
class Counter : CounterBase
{
public int Count { get; private set; }
/// <summary>
/// 非Lock关键字减法
/// </summary>
public override void Decrement()
{
Count--;
}
/// <summary>
/// 非lock关键字加法
/// </summary>
public override void Increment()
{
Count++;
}
}
/// <summary>
/// 使用Lock关键字的类
/// </summary>
class CounterWidthLock : CounterBase
{
/// <summary>
/// 判断是否锁定资源
/// </summary>
public readonly object _syncRoot = new object(); public int Count { get; private set; } /// <summary>
/// Lock关键字减法
/// </summary>
public override void Decrement()
{
lock (_syncRoot)//lock关键字,锁定资源
{
Count--;
}
}
/// <summary>
/// lock关键字加法
/// </summary>
public override void Increment()
{
lock (_syncRoot)//lock关键字,锁定资源
{
Count++;
}
}
}
/// <summary>
/// 测试主类
/// </summary>
class Program
{
/// <summary>
/// 测试主程序入口
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Console.WriteLine("不使用Lock关键字 Counter类"); var NotLockClass = new Counter(); var t1 = new Thread(() =>TestCounter(NotLockClass)); var t2 = new Thread(() => TestCounter(NotLockClass)); var t3= new Thread(() => TestCounter(NotLockClass)); t1.Start(); t2.Start(); t3.Start(); t1.Join(); t2.Join(); t3.Join(); Console.WriteLine($"最终输出结果是{NotLockClass.Count}"); Console.ReadKey();
}
static void TestCounter(CounterBase counterBase)
{
for(int i=;i<;i++)
{
counterBase.Increment();//加法 counterBase.Decrement();//减法
}
}
}
上面这一部分是,未使用lock关键字的代码。
结果也如同书上说count是未定。多次启动程序 结果也是不一样的。正确的结果应该是0,加一次见一次正好分别是5K次。
仔细观察代码,得到这样子的结果也是肯定的。
首先:
var NotLockClass = new Counter(); var t1 = new Thread(() =>TestCounter(NotLockClass)); var t2 = new Thread(() => TestCounter(NotLockClass)); var t3= new Thread(() => TestCounter(NotLockClass));
这个部分,是三个实例的Thread共享了一个Counter的实例化对象。
那么,要肯定的是这个Counter是一个引用类型
那么引用类型发挥在哪里呢?
t1.Start(); t2.Start(); t3.Start(); t1.Join(); t2.Join(); t3.Join();
这里。
首先是T1 T2 T3三个线程启动。
然后是等待线程完成。
让结果变得不固定的原因就在这里。
我们先把方法TestCounter改造一下
static void TestCounter(CounterBase counterBase)
{
for(int i=;i<;i++)
{
counterBase.Increment();//加法
Console.WriteLine($"加法启动次序{i}");
counterBase.Decrement();//减法
Console.WriteLine($"减法启动次序{i}");
}
}
加入一下两个控制台输出语句。
然后启动程序。
会发现有很多重复的顺序。而这个就是资源抢夺。
为了更清楚的看执行的过程,我们再来改造一下。
static void TestCounter(Counter counter)
{
for(int i=;i<;i++)
{
counter.Increment();//加法
Console.WriteLine($"加法启动次序{i},当前结果是{counter.Count}");
counter.Decrement();//减法
Console.WriteLine($"减法启动次序{i},当前结果是{counter.Count}");
}
}
之后在启动
资源抢夺还是很严重的。 不过你们有没有发现。方法改造之后的结果和正确结果很相近了。不是-1,就是-2.这是一个很有意思的现象。
如果你将代码改造成:
class Counter : CounterBase
{
public int Count { get; private set; }
/// <summary>
/// 非Lock关键字减法
/// </summary>
public override void Decrement()
{
Console.WriteLine($"减法 当前{Count}");
Count--;
}
/// <summary>
/// 非lock关键字加法
/// </summary>
public override void Increment()
{
Console.WriteLine($"加法 当前{Count}");
Count++;
}
}
结果:
竟然会出现正确答案,而且几率很高。得到-1这个答案我运行了很多次。-2更是看运气。
为什么会出现这个现象。我觉得是可以程序的方法可运行时间有一定关系吧。时间可能是多了一下。资源调度上面可能会分配。也可能是线程的原因。这个问题真的很有意思。
不过还是先放放。
我们依旧是得到未使用lock关键字,资源抢夺很严重!
那么我们来看看是使用lock关键字的部分。
改造代码:
static void Main(string[] args)
{
Console.WriteLine("使用Lock关键字 CounterWidthLock类"); var UseLockClass = new CounterWidthLock(); var t1 = new Thread(() =>TestCounter(UseLockClass)); var t2 = new Thread(() => TestCounter(UseLockClass)); var t3= new Thread(() => TestCounter(UseLockClass)); t1.Start(); t2.Start(); t3.Start(); t1.Join(); t2.Join(); t3.Join(); Console.WriteLine($"最终输出结果是{UseLockClass.Count}"); Console.ReadKey();
}
肯定是正确答案。
我们改造一下代码:和上一样:
static void TestCounter(CounterBase counterBase)
{
for(int i=;i<;i++)
{
counterBase.Increment();//加法
Console.WriteLine($"加法启动次序{i}");
counterBase.Decrement();//减法
Console.WriteLine($"减法启动次序{i}"); }
}
看一下是否会出现资源抢夺
其实还是会出现的。
但是为什么结果会是正确的呢?
我们再来改造一下:
static void TestCounter(CounterWidthLock counterwidthlock)
{
for(int i=;i<;i++)
{
counterwidthlock.Increment();//加法
Console.WriteLine($"加法启动次序{i},当前的结果是{counterwidthlock.Count}");
counterwidthlock.Decrement();//减法
Console.WriteLine($"减法启动次序{i},当前的结果是{counterwidthlock.Count}"); }
}
很明显,执行的顺序还是混乱。但是为什么结果却是正确呢?
我们来正式的讲讲lock关键字了
第一步 是lock建立互斥锁。
第二步执行lock内的方法执行完毕之后,
第三步释放lock互斥锁。
这一个过程中,只有一个线程能访问,如果有其他线程访问那就必须等待第一个线程执行完,并释放lock。
那么在代码中
class CounterWidthLock : CounterBase
{
/// <summary>
/// 判断是否锁定资源
/// </summary>
public readonly object _syncRoot = new object(); public int Count { get; private set; } /// <summary>
/// Lock关键字减法
/// </summary>
public override void Decrement()
{
lock (_syncRoot)//lock关键字,锁定资源
{
Count--;
}
}
/// <summary>
/// lock关键字加法
/// </summary>
public override void Increment()
{
lock (_syncRoot)//lock关键字,锁定资源
{
Count++;
}
}
}
6:锁
15,25均为lock关键字。
那么什么是锁?
可以理解为一个唯一的资源,对象。这个对象最好不是公开的。 公开的话 会造成很多不便。
也就是T1 T2 T3 虽然都同时进行了TestCounter的方法。
但因为lock的存在,一个线程在执行加减的时候,其他线程是不可以干预的。也就是T1 执行加减,也许执行了三四次,T2 T3一直在等待。三个线程都是彬彬有礼等待其他来完成他们自己的事情,之后在是自己的。虽然谦让,但也有自己的原则,就是自己在做的时候,别人是不可以干预的
《C#多线程编程实战》1.10 lock关键字的更多相关文章
- C#多线程编程实战(二)
1.1 简介 为了防止一个应用程序控制CPU而导致其他应用程序和操作系统本身永远被挂起这一可能情况,操作系统不得不使用某种方式将物理计算分割为一些虚拟的进程,并给予每个执行程序一定量的计算能力.此外操 ...
- 《Java多线程编程实战指南(核心篇)》阅读笔记
<Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...
- Java多线程编程实战指南(核心篇)读书笔记(二)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(一)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76422930冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(五)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(四)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76690961冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(三)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76686044冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- ASP.Net教程系列:多线程编程实战(一)
Web开发中使用多线程可以增强用户体验,尤其是多用户.多任务.海量数据和资源紧张的情况下.所以我们的ASP.Net教程设立多线程编程实战专题.下面这些代码范例都是入门级的,希望对对大家学习ASP.Ne ...
- Java多线程编程实战02:多线程编程模型
多线程编程模型 线程安全名词 串行.并发和并行 串行:一个人,将任务一个一个完成 并发:一个人,有策略地同时做多件事情 并行:多个人,每人做一个事情 竞态 名词 竞态:计算结果的正确性与时间有关的现象 ...
- Java多线程编程实战读书笔记(一)
多线程的基础概念本人在学习多线程的时候发现一本书——java多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.
随机推荐
- zabbix snmp 协议监控 dell iRDAC
转摘:http://blog.csdn.net/wanglei_storage/article/details/52789921 http://blog.csdn.net/wanglei_storag ...
- 配置ElasticSearch快捷启动
在/etc/init.d目录下新建文件elasticsearch #!/bin/sh #chkconfig: 2345 80 05 #description: es #export JAVA_HOME ...
- python开发模块基础:序列化模块json,pickle,shelve
一,为什么要序列化 # 将原本的字典.列表等内容转换成一个字符串的过程就叫做序列化'''比如,我们在python代码中计算的一个数据需要给另外一段程序使用,那我们怎么给?现在我们能想到的方法就是存在文 ...
- python学习(九) 魔法方法、属性和迭代器
9.1 准备工作 python 3.0的所有类都会隐式地成为object的子类. 9.2 构造方法 在python中创建一个构造方法:只要把init方法的名字修改为魔法版本__init__即可. &g ...
- python学习(一) 基础知识
开始学习<Python基础教程> 1.2 交互式解释器 按照书上的例子敲了个最简单的print函数,居然报错: >>> print "fsdfs"Sy ...
- ceph 创建和删除osd
ceph 创建和删除osd 1.概述 本次主要是使用ceph-deploy工具和使用ceph的相关命令实现在主机上指定磁盘创建和删除osd,本次以主机172.16.1.96(主机名ha ...
- 3.Periodic Tasks
celery beat是一个调度器,它可以周期内指定某个worker来执行某个任务.如果我们想周期执行某个任务需要增加beat_schedule配置信息. broker_url='redis:/ ...
- C# 泛型类型参数的约束
在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制.如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误.这些限制称为约束.约束是使用 where 上 ...
- PHP下的手机号码效验
手机号是否合法有效,基本都是用正则匹配的,所以正则表达式是关键,可以用到java.c#等语言里. /** * 验证手机号是否合法 * * @param string $mobile * 验证的手机号 ...
- zookeeper集群安装的奇怪现象
zookeeper:配置的集群信息是domain:端口2888:端口3888: domain为内网静态ip:每次启动都不能相互连接报错误: [myid:3] - WARN [WorkerSende ...