(67)多核同步,lock 总线锁 ,自己实现临界区
一、多核同步问题
单条汇编指令可能被多个CPU同时执行,此时就可能会引发安全问题。
考虑下面的指令:
INC DWORD PTR DS:[0x12345678]
如果两个CPU同时执行该指令,[0x12345678] 的初始值是0,那么两个CPU执行后,本应该 [0x12345678] 是2,结果却有可能是1。
为了解决这个问题,可以使用 LOCK 对某个内存地址“加锁”,将指令修改成如下:
LOCK INC DWORD PTR DS:[0x12345678]
添加 LOCK 之后,保证了多个CPU不能同时对这条指令进行访问,这也就实现了安全保证。
单核模式下,单条汇编指令一定是满足原子性的,所以根本用不到 LOCK ,我们可以验证一下这个说法,首先了解一下 WINDOWS 提供的原子性操作API。
二、原子操作API
这里列举部分:
- InterlockedIncrement
- InterlockedExchangeAdd
- InterlockedDecrement
- InterlockedFlushSList
- InterlockedExchange
- InterlockedPopEntrySList
- InterlockedCompareExchange
- InterlockedPushEntrySList
先挑一个 InterlockedIncrement 作简单介绍,这个API可以在三环使用的,作用是对某个变量+1,满足原子性,就是说不会有多核或者多线程的同步安全问题。它接收一个地址作为参数,对它里面的值+1,然后返回+1 后的结果,这个宏的声明我们也是可以找到的:
LONG InterlockedIncrement(LPLONG lpAddend // variable to increment
);
下面我们就来对比看看单核和多核模式下,这个API的实现有什么区别。
首先我们打开单核模式的内核文件,找到 InterlockedIncrement ,观察其代码:
; __fastcall __InterlockedIncrement(x)
public @__InterlockedIncrement@4
@__InterlockedIncrement@4 proc near
mov eax, 1
xadd [ecx], eax
inc eax
retn
@__InterlockedIncrement@4 endp
xadd是先交换两个数,然后把求和的结果存到原来的第一个操作数里,结合上面的介绍,这个函数应该不难理解。
然后打开多核模式下的内核文件,我说明一下怎么找多核内核文件和符号文件:
ntoskrnl - 单处理器,不支持PAE
ntkrnlpa - 单处理器,支持PAE
ntkrnlmp - 多处理器,不支持PAE
ntkrpamp - 多处理器,支持PAE
我们在符号文件的目录下能找到符号文件,比如我要多核PAE,那就是 ntkrpamp.pdb ,然后把虚拟机设置改成多核,去 system32 里取 ntkrnlpa.exe ,就好了。
打开内核文件,载入符号,找到 InterlockedIncrement
; __fastcall __InterlockedIncrement(x)
public @__InterlockedIncrement@4
@__InterlockedIncrement@4 proc near
mov eax, 1
lock xadd [ecx], eax
inc eax
retn
@__InterlockedIncrement@4 endp
发现和单核的区别就是 xadd 指令加了 lock 。这样就保证了多个CPU不能同时读这个指令的内存,也就不能同时执行该指令了,这也就保证了对 [ecx] 的同步访问。
三、自己实现临界区(不使用xadd)
所谓临界区,可以理解成某段指令同一时刻只能有一个CPU/线程在执行,实现方法是多样的。
比如我可以定义一个全局变量 CriticalLock 初始化为 -1,表示当前没有占用,可以访问临界区。
每个线程的第一条指令都是 inc CriticalLock ,然后判断如果等于0,表示自己是第一个线程,就可以执行业务代码;否则就算没抢到,把 CriticalLock 减回去,循环刚才的步骤直到 CriticalLock 等于0.
听起来没什么问题,下面给出一段代码,起了10个线程,每个线程给全局变量 g_value 加1,重复十次,理论上最后 g_value 应该等于 100.
错误的实现
#include "stdafx.h"
#include <windows.h>int g_value = 0;int CriticalLock = -1; // -1表示可以进入临界区DWORD WINAPI MyThread(LPVOID TID)
{for (int i = 0; i < 10; i++){CriStart:Sleep(20); // 提高效率__asm{inc [CriticalLock];jz CriEnd;dec [CriticalLock];jmp CriStart;}
CriEnd:// 耗时业务g_value+=3;Sleep(20);g_value-=2;printf("%d\n", g_value);__asm dec [CriticalLock];}return 0;
}int _tmain(int argc, _TCHAR* argv[])
{for (int i = 0; i < 10; i++){CreateThread(0,0,MyThread,(LPVOID)i,0,0);}getchar();printf("所有线程结束,g_value = %d\n", g_value);getchar();return 0;
}
可以看到程序输出是错的,原因是当前机器是多核的,多个CPU同时调用不同的线程,同时对 CrititalLock 进行读写,就会出现同步安全问题。(这段代码在单核模式下是正确的)
错误的实现2
#include "stdafx.h"
#include <windows.h>int g_value = 0;int CriticalLock = -1; // -1表示可以进入临界区DWORD WINAPI MyThread(LPVOID TID)
{for (int i = 0; i < 10; i++){CriStart:Sleep(20); // 提高效率__asm{lock inc [CriticalLock];jz CriEnd;lock dec [CriticalLock];jmp CriStart;}
CriEnd:// 耗时业务g_value+=3;Sleep(20);g_value-=2;printf("%d\n", g_value);__asm lock dec [CriticalLock];}return 0;
}int _tmain(int argc, _TCHAR* argv[])
{for (int i = 0; i < 10; i++){CreateThread(0,0,MyThread,(LPVOID)i,0,0);}getchar();printf("所有线程结束,g_value = %d\n", g_value);getchar();return 0;
}
正确的实现(xchg)
#include "stdafx.h"
#include <windows.h>int g_value = 0;int CriticalLock = 0;DWORD WINAPI MyThread(LPVOID TID)
{for (int i = 0; i < 10; i++){CriStart:__asm{mov eax,1;lock xchg [CriticalLock],eax;cmp eax,1;jnz CriEnd;}Sleep(20);__asm jmp CriStart;
CriEnd:// 耗时业务g_value+=3;Sleep(20);g_value-=2;printf("%d\n", g_value);__asm dec [CriticalLock];}return 0;
}int _tmain(int argc, _TCHAR* argv[])
{for (int i = 0; i < 10; i++){CreateThread(0,0,MyThread,(LPVOID)i,0,0);}getchar();printf("所有线程结束,g_value = %d\n", g_value);getchar();return 0;
}
(67)多核同步,lock 总线锁 ,自己实现临界区相关推荐
- Windows驱动开发学习笔记(七)—— 多核同步内核重载
Windows驱动开发学习笔记(七)-- 多核同步 基础知识 并发与同步 分析 InterlockedIncrement 原子操作相关API 内核文件 多核同步 临界区 示例一:错误的临界区 示例二: ...
- 线程同步 – lock和Monitor
在多线程代码中,多个线程可能会访问一些公共的资源(变量.方法逻辑等等),这些公共资源称为临界区(共享区):临界区的资源是不安全,所以需要通过线程同步对多个访问临界区的线程进行控制. 同样,有些时候我们 ...
- 服务器多个网站开启quarz,GitHub - WuLex/QuartzSynchroData: 多个不同站点服务器数据同步到总服务器(数据中心)...
QuartzSynchroData 多个不同站点服务器sqlserver数据库 同步到总服务器上的数据库 (数据中心) 目前支持40多个数据库(在数据库配置表DBconfig配置了)的同步,每天每个服 ...
- 多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)
多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上) 转自 http://www.cnblogs.com/freshman0216/archive/2008/07/27/ ...
- C语言线程lock与unlock,谈谈线程同步Lock和unLock
Lock可以使用Condition进行线程之间的调度,它有更好的灵活性,而且在一个对象里面可以有多个Condition(即对象监视器),则线程可以注册在不同的Condition,从而可以 有选择性的调 ...
- Java 多线程和并发编程:(二)线程同步 Lock 锁
线程同步 Lock 锁 1.Lock 锁 2.步骤 3.Lock 与 synchronized 的区别 1.Lock 锁 Lock 锁:对需要上锁的地方上锁 JDK1.5 后新增的功能 与 Synch ...
- python类库32[多线程同步Lock+RLock+Semaphore+Event]
2019独角兽企业重金招聘Python工程师标准>>> 一 多线程同步 由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地 ...
- python线程安全的计数器_Python多线程同步Lock、RLock、Semaphore、Event实例
一.多线程同步 由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地发挥多核cpu的资源.大部分情况都推荐使用多进程. python的多线程的 ...
- python 线程 的类库_python类库32[多线程同步Lock+RLock+Semaphore+Event]
多线程基础:python类库32[多线程同步] 一 多线程同步 由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地发挥多核cpu的资源.大部 ...
最新文章
- php imagecopy 用法,php使用imagecopymerge()函数创建半透明水印
- a 中调用js的几种方法整理及使用推荐
- java amp amp 怎么用,java中amp;与amp;amp;的区别
- javascript array添加图片_史上最全的web前端面试题汇总及答案JavaScript之二(二)...
- 语音识别HCLG解码
- c# 以太坊代币_C代币
- 敏感词过滤算法:前缀树算法
- 思科 GNS3 配置 链路捆绑
- linux采用scp命令拷贝文件到本地,拷贝本地文件到远程服务器
- android 图片 写入文件格式,android实现将位置信息写入JPEG图片文件
- (10)Redis------必须知道的基础内容
- Snagit 2020 for mac(最好用的屏幕截图软件)
- 云服务器Tomcat版本升级(Tomcat6升级至Tomcat7和Tomcat8)问题总结
- android exoplayer 直播流,android – Exoplayer自适应hls流媒体
- 农产品加工进销存单_实用必看!手把手教你制作进销存出入库表格
- 在线下单系统think php,昱杰订单管理系统(ThinkPHP版) v19.0
- 论文解读--Multi-class Road User Detection with 3+1D Radar in the View-of-Delft Dataset
- PDF可以被压缩吗,是如何实现的?
- 【JAVA】Java中switch的用法。
- 练习print函数的使用(python)
热门文章
- Dataset:GiveMeSomeCredit数据集的简介、下载、使用方法之详细攻略
- 成功解决If your current network has https://www.anaconda.com blocked, please filea support request with
- GPU:nvidia-smi的简介、安装、使用方法之详细攻略
- Paper:《A Unified Approach to Interpreting Model Predictions—解释模型预测的统一方法》论文解读与翻译
- WSL:WSL(Windows Subsystem for Linux)的简介、安装、使用方法之详细攻略
- 成功解决ModuleNotFoundError: No module named 'keras_retinanet.utils.compute_overlap'
- DL之CNN:基于CNN-RNN(GRU,2)算法(keras+tensorflow)实现不定长文本识别
- Love:程序猿的方式~【情人节520—我爱你】~动画加音效 → 那些年最浪漫的表白(帮你得到你的她)
- 【Python】特征选择方法
- 『转载』在vs2008(2005)winform中,打开office文档