《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多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.
随机推荐
- ll | wc -l的陷阱
在平时我们可能经常使用ls和wc命令来统计某个文件夹下指定类型文件的个数.今天在使用的时候发现ls和ll出来的结果不一样. dwapp@pttest1:/home/dwapp/joe.wangh/te ...
- java常见的几种调用机制(同步调用,异步调用,回调)
1.同步调用 同步调用是最基本的调用方式,对象b中的方法直接调用对象a的方法,这个时候程序会等待对象a的方法执行完返回结果之后才会继续往下走. 代码如下: public class A {public ...
- GET、POST详解 --转自http://hi.baidu.com/richarwu/item/bd43633a6ba62986b611dbcd
HTTP Get,Post请求详解 请求类型 三种最常见的请求类型是:GET,POST 和 HEAD GET:获取一个文档 大部分被传输到浏览器的html,images,js,css, … 都是通过G ...
- infos
C++文件流 iostream 提供了cin cout 分别用于从标准输入读取流和向标准输出写入流cout 标准输出 屏幕 输出写入fstream文件流 cin 从标准输入读取cout 向标准输入写入 ...
- 服务器实现处理GET和POST
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.Inp ...
- Java中的Constants类
1.遇到代码中的问题 String userName = (String) request.getParameter("userName"); String passWord = ...
- error:crosses initialization of ...的解决办法
switch(c) { case 0x01: int temp = a + b; .... break; case 0x02: break; default:break; } 此时会报如题所示错误 原 ...
- mysql主从延迟
1. MySQL数据库主从同步延迟原理.要说延时原理,得从mysql的数据库主从复制原理说起,mysql的主从复制都是单线程的操作,主 库对所有DDL和DML产生binlog,binlog是顺序写,所 ...
- CodeForces - 721E
题目大意 现有一个长为 L的数轴,你要从0走到 L 给出n个互不相交的可行域. 你要选择长度为p的段,要求每一个段都要在可行域内. 选完一段之后下一段要么和其相接,要么和其间距至少为t,求问最多能选择 ...
- c++ 多态问题(在虚函数里调用虚函数)
最近在看cocos2d-x的源码,非常感激cocos2d作者的开源精神.在看代码的过程中感觉两个方向让我受益,1.把之前从书中看到的c++知识,明白了怎么运用.2.学习作者驾驭代码的巧妙方法. 看co ...