多线程的那点事儿(之数据互斥)
在多线程存在的环境中,除了堆栈中的临时数据之外,所有的数据都是共享的。如果我们需要线程之间正确地运行,那么务必需要保证公共数据的执行和计算是正确的。简单一点说,就是保证数据在执行的时候必须是互斥的。否则,如果两个或者多个线程在同一时刻对数据进行了操作,那么后果是不可想象的。
也许有的朋友会说,不光数据需要保护,代码也需要保护。提出这个观点的朋友只看到了数据访问互斥的表象。在程序的运行空间里面,什么最重要的呢?代码吗?当然不是。代码只是为了数据的访问存在的。数据才是我们一切工作的出发点和落脚点。
那么,有什么办法可以保证在某一时刻只有一个线程对数据进行操作呢?四个基本方法:
(1)关中断
(2)数学互斥方法
(3)操作系统提供的互斥方法
(4)cpu原子操作
为了让大家可以对这四种方法有详细的认识,我们可以进行详细的介绍。
(1)关中断
要让数据在某一时刻只被一个线程访问,方法之一就是停止线程调度就可以了。那么怎样停止线程调度呢?那么关掉时钟中断就可以了啊。在X86里面的确存在这样的两个指令,
- #include <stdio.h>
- int main()
- {
- __asm{
- cli
- sti
- }
- return 1;
- }
其中cli是关中断,sti是开中断。这段代码没有什么问题,可以编过,当然也可以生成执行文件。但是在执行的时候会出现一个异常告警:Unhandled exception in test.exe: 0xC0000096: Privileged Instruction。告警已经说的很清楚了,这是一个特权指令。只有系统或者内核本身才可以使用这个指令。
不过,大家也可以想象一下。因为平常我们编写的程序都是应用级别的程序,要是每个程序都是用这些代码,那不乱了套了。比如说,你不小心安装一个低质量的软件,说不定什么时候把你的中断关了,这样你的网络就断了,你的输入就没有回应了,你的音乐什么都没有了,这样的环境你受的了吗?应用层的软件是千差万别的,软件的水平也是参差不齐的,所以系统不可能相信任何一个私有软件,它相信的只是它自己。
(2)数学方法
假设有两个线程(a、b)正要对一个共享数据进行访问,那么怎么做到他们之间的互斥的呢?其实我们可以这么做,
- unsigned int flag[2] = {0};
- unsigned int turn = 0;
- void process(unsigned int index)
- {
- flag[index] = 1;
- turn = index;
- while(flag[1 - index] && (turn == index));
- do_something();
- flag[index] = 0;
- }
其实,学过操作系统的朋友都知道,上面的算法其实就是Peterson算法,可惜它只能用于两个线程的数据互斥。当然,这个算法还可以推广到更多线程之间的互斥,那就是bakery算法。但是数学算法有两个缺点:
a)占有空间多,两个线程就要flag占两个单位空间,那么n个线程就要n个flag空间,
b)代码编写复杂,考虑的情况比较复杂
(3)系统提供的互斥算法
系统提供的互斥算法其实是我们平时开发中用的最多的互斥工具。就拿windows来说,关于互斥的工具就有临界区、互斥量、信号量等等。这类算法有一个特点,那就是都是依据系统提高的互斥资源,那么系统又是怎么完成这些功能的呢?其实也不难。
系统加锁过程,
- void Lock(HANDLE hLock)
- {
- __asm {cli};
- while(1){
- if(/* 锁可用*/){
- /* 设定标志,表明当前锁已被占用 */
- __asm {sti};
- return;
- }
- __asm{sti};
- schedule();
- __asm{cli};
- }
- }
系统解锁过程,
- void UnLock(HANDLE hLock)
- {
- __asm {cli};
- /* 设定标志, 当前锁可用 */
- __asm{sti};
- }
上面其实讨论的就是一种最简单的系统锁情况。中间没有涉及到就绪线程的压入和弹出过程,没有涉及到资源个数的问题,所以不是很复杂。朋友们仔细看看,应该都可以明白代码表达的是什么意思。
(4)CPU的原子操作
因为在多线程操作当中,有很大一部分是比较、自增、自减等简单操作。因为需要互斥的代码很少,所以使用互斥量、信号量并不合算。因此,CPU厂商为了开发的方便,把一些常用的指令设计成了原子指令,在windows上面也被称为原子锁,常用的原子操作函数有
- InterLockedAdd
- InterLockedExchange
- InterLockedCompareExchange
- InterLockedIncrement
- InterLockedDecrement
- InterLockedAnd
- InterLockedOr
多线程的那点事儿(之数据互斥)相关推荐
- 多线程操作数据库时为了防止数据的增删改的混乱该在数据库层还是程序层面上进行同步?
多线程操作数据库时为了防止数据的增删改的混乱该在数据库层还是程序层面上进行同步? [问题点数:60分,结帖人jiao_zg] 不显示删除回复 显示所有回复 显示星级回复 显示得分回复 只显示楼主 收藏 ...
- 服务器多线程定时发送消息,C++Socket 多线程编程 定时和定量发送数据
C++Socket 多线程编程 定时和定量发送数据 C++Socket 多线程编程 定时和定量发送数据 C++ Socket API中 发送函数Send和接收函数Recv函数的调用不一定是1对1的关系 ...
- 如何掌握java多线程,高并发,大数据方面的技能?
https://www.zhihu.com/question/27575123 如何掌握java多线程,高并发,大数据方面的技能? 因为想进入互联网公司,然后发现互联网类型的公司问的主要问题都离不开这 ...
- Java使用多线程做批处理(查询大量数据)
Java使用多线程做批处理(查询大量数据) Java使用多线程做批处理(查询大量数据) Java使用多线程做批处理(查询大量数据) 前言背景 Java使用多线程的条件 操作流程 前言背景 什么是进程: ...
- Java多线程(Thread)间通信和共享数据
多线程间如何通信和共享数据,肯定好多小伙伴都不知道, 或者很久之前知道,现在已经忘却,现在我和大家一起复习一下. 一.线程间通信 1.线程间通信:A执行完,B才执行 /*** 线程间通信:A执行完,B ...
- Java多线程编程实战:模拟大量数据同步
背景 最近对于 Java 多线程做了一段时间的学习,笔者一直认为,学习东西就是要应用到实际的业务需求中的.否则要么无法深入理解,要么硬生生地套用技术只是达到炫技的效果. 不过笔者仍旧认为自己对于多线程 ...
- python多线程爬取斗图啦数据
python多线程爬取斗图啦网的表情数据 使用到的技术点 requests请求库 re 正则表达式 pyquery解析库,python实现的jquery threading 线程 queue 队列 ' ...
- 秒杀多线程第七篇 经典线程同步 互斥量Mutex
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...
- C#多线程时对同一资源加锁实现互斥访问
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
最新文章
- feign调用多个服务_Spring Cloud多个微服务之间调用代码实例
- vue-cli构建项目
- outlook地址薄导入到FOXMAIL
- pca降维的基本思想_一文读懂 PCA 降维算法
- try/ catch/ finally, 你不知道的细节,很骚!
- 多选取值_机制砂如何控制MB值和石粉含量
- vue键盘抬起_vue实现编辑器键盘抬起时内容跟随光标距顶位置向上滚动效果
- 德力西双电源自动转换开关说明书_今日优品:南开区FLQ3-25双电源自动转换开关全国服务-老友网...
- 获取点击按钮的元素_怎么按顺序自动点击网页所有链接
- 190308每日一句
- Unity实现安卓虚拟摇杆多点触控
- GreenSock (TweenMax) 极简入门指南
- 局域网内连接其他计算机共享的打印机
- 提升update执行效率
- 解决linux:docker-compose: Permission denied
- 记录贴/阴阳师core loop
- wpf 三种 loading 样式
- mysql emoy表情_“笑哭”emoji表情过时了,一波“捂脸”大军正在路上
- 各种相似度计算的python实现
- 4.15 最短路 题