《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多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.
随机推荐
- 腾讯云搭建php环境
1.安装搭建论坛必要的软件 apache php mysql CentOS系统我们可以直接使用 yum install 的方式进行软件安装,腾讯云有提供软件安装源,是同步CentOS官方的安装源, ...
- 解决在“Resources”参数中指定了项“obj\Debug\KaiShiHID.Form1.resources”多次。“Resources”参数不支持重复项
错误截图: 发生原因描述: 窗体Form1, 有一个分部类; 叫做FormPartial.cs; 双击了FormPartial这个窗体, 然后生成了一个Load事件, 即时就报了编译错误, 然后就删除 ...
- 五 搭建kafka集群
1 下载 wget http://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.0.0/kafka_2.12-2.0.0.tgz 2 tar -zxv ...
- VS配置附加包含目录技巧
把include文件夹(里面是某个库的头文件)拷到自己的项目中,添加头文件时需要使用#include"include\xxx.h"方式,如果打算使用#include"xx ...
- js是这样判断值的数据类型的
js如何判定给定值的数据类型? typeof typeof "a" // "string" typeof 1 // "number" typ ...
- dubbo学习 一 dubbo概述
1,背景 1,网站刚开时候的时候可能所有的功能业务都在一个应用里面 2,当业务不断复杂,流量不断增多的时候,就需要将原先的一个应用划分成多个独立的应用. 3,当分出来的业务越来越多的时候,应用 ...
- 每天一个Linux命令 - 【chkconfig】
[命令]:chkonconfig [语法]:chkconfig [选项] [功能介绍]:chkconfig 命令是Redhat兼容的Linux发行版中的系统服务管理工具,它可以查询和更新不同的运行等一 ...
- Python基础—流程控制
一.Python流程控制 计算机程序在解决某个具体问题时,包括三种情形,即顺序执行所有的语句.选择执行部分的语句和循环执行部分语句,这正好对应着程序设计中的三种程序执行结构流程:顺序结构.选择结构和循 ...
- 【原创】2. MYSQL++之Connect类型
mysqlpp:: Connect类型主要负责连接事宜,这是在所有开始mysql操作之前必须进行的(这是句废话). 该类型的主要的结果如下所示 mysqlpp::OpeitonalExceptions ...
- std:: lower_bound std:: upper_bound
std:: lower_bound 该函数返回范围内第一个不小于(大于或等于)指定val的值.如果序列中的值都小于val,则返回last.序列应该已经有序! eg: #include <iost ...