《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多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.
随机推荐
- shutil模块,ZipFile 和 TarFile 两个模块
高级的文件.文件夹.压缩包处理模块 shutil.copyfileobj(fsrc, fdst[, length])将文件内容拷贝到另一个文件中,可以部分内容 shutil.copyfile(src, ...
- Javascript 推荐一个图形化展示库
觉得这个库不错: http://almende.github.io/chap-links-library/index.html
- netty实现远程调用RPC功能
netty实现远程调用RPC功能 依赖 服务端功能模块编写 客户端功能模块编写 netty实现远程调用RPC功能 PRC的功能一句话说白了,就是远程调用其他电脑的api 依赖 <dependen ...
- BeX5 常见问题解决办法
1.获取当前Activity的名称 Activity activity = ProcessUtils.getActivityInProcessContext(); String activityNam ...
- VS2008与MATLAB R2007a混合编程配置过程
系统平台:windows xp sp2, visual studio 2008 professional, matlab R2007a 首先,为了使matlab 能够找到vs2008编译器,需要下载以 ...
- jquery slideDown效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- 嵌入式Linux启动配置文件及脚本分…
使用Busybox制作根文件系统时,/etc目录非常重要,它包含了嵌入式Linux启动所需的配置文件及脚本.由于init进程,或者说linuxrc程序会解析inittab文件,因此就从/etc/ini ...
- Mac设置Root密码
[Mac设置Root密码] Mac系统重新设置root用户密码 如果不知道root用户密码,需要重设. 命令[sudo passwd root ] 然后提示你输入当前登录用户密码,通过以后, ...
- 【bzoj1056】排名系统
1056: [HAOI2008]排名系统 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2195 Solved: 623[Submit][Statu ...
- C++中内存区域的划分
栈存储区 那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区.里面的变量通常是局部变量.函数参数等. 堆存储区(自由存储区) 那些由new或者malloc分配的内存块,他们的释放编译器 ...