一、多核同步问题

单条汇编指令可能被多个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 总线锁 ,自己实现临界区相关推荐

  1. Windows驱动开发学习笔记(七)—— 多核同步内核重载

    Windows驱动开发学习笔记(七)-- 多核同步 基础知识 并发与同步 分析 InterlockedIncrement 原子操作相关API 内核文件 多核同步 临界区 示例一:错误的临界区 示例二: ...

  2. 线程同步 – lock和Monitor

    在多线程代码中,多个线程可能会访问一些公共的资源(变量.方法逻辑等等),这些公共资源称为临界区(共享区):临界区的资源是不安全,所以需要通过线程同步对多个访问临界区的线程进行控制. 同样,有些时候我们 ...

  3. 服务器多个网站开启quarz,GitHub - WuLex/QuartzSynchroData: 多个不同站点服务器数据同步到总服务器(数据中心)...

    QuartzSynchroData 多个不同站点服务器sqlserver数据库 同步到总服务器上的数据库 (数据中心) 目前支持40多个数据库(在数据库配置表DBconfig配置了)的同步,每天每个服 ...

  4. 多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)

    多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上) 转自 http://www.cnblogs.com/freshman0216/archive/2008/07/27/ ...

  5. C语言线程lock与unlock,谈谈线程同步Lock和unLock

    Lock可以使用Condition进行线程之间的调度,它有更好的灵活性,而且在一个对象里面可以有多个Condition(即对象监视器),则线程可以注册在不同的Condition,从而可以 有选择性的调 ...

  6. Java 多线程和并发编程:(二)线程同步 Lock 锁

    线程同步 Lock 锁 1.Lock 锁 2.步骤 3.Lock 与 synchronized 的区别 1.Lock 锁 Lock 锁:对需要上锁的地方上锁 JDK1.5 后新增的功能 与 Synch ...

  7. python类库32[多线程同步Lock+RLock+Semaphore+Event]

    2019独角兽企业重金招聘Python工程师标准>>> 一 多线程同步 由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地 ...

  8. python线程安全的计数器_Python多线程同步Lock、RLock、Semaphore、Event实例

    一.多线程同步 由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地发挥多核cpu的资源.大部分情况都推荐使用多进程. python的多线程的 ...

  9. python 线程 的类库_python类库32[多线程同步Lock+RLock+Semaphore+Event]

    多线程基础:python类库32[多线程同步] 一 多线程同步 由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地发挥多核cpu的资源.大部 ...

最新文章

  1. php imagecopy 用法,php使用imagecopymerge()函数创建半透明水印
  2. a 中调用js的几种方法整理及使用推荐
  3. java amp amp 怎么用,java中amp;与amp;amp;的区别
  4. javascript array添加图片_史上最全的web前端面试题汇总及答案JavaScript之二(二)...
  5. 语音识别HCLG解码
  6. c# 以太坊代币_C代币
  7. 敏感词过滤算法:前缀树算法
  8. 思科 GNS3 配置 链路捆绑
  9. linux采用scp命令拷贝文件到本地,拷贝本地文件到远程服务器
  10. android 图片 写入文件格式,android实现将位置信息写入JPEG图片文件
  11. (10)Redis------必须知道的基础内容
  12. Snagit 2020 for mac(最好用的屏幕截图软件)
  13. 云服务器Tomcat版本升级(Tomcat6升级至Tomcat7和Tomcat8)问题总结
  14. android exoplayer 直播流,android – Exoplayer自适应hls流媒体
  15. 农产品加工进销存单_实用必看!手把手教你制作进销存出入库表格
  16. 在线下单系统think php,昱杰订单管理系统(ThinkPHP版) v19.0
  17. 论文解读--Multi-class Road User Detection with 3+1D Radar in the View-of-Delft Dataset
  18. PDF可以被压缩吗,是如何实现的?
  19. 【JAVA】Java中switch的用法。
  20. 练习print函数的使用(python)

热门文章

  1. Dataset:GiveMeSomeCredit数据集的简介、下载、使用方法之详细攻略
  2. 成功解决If your current network has https://www.anaconda.com blocked, please filea support request with
  3. GPU:nvidia-smi的简介、安装、使用方法之详细攻略
  4. Paper:《A Unified Approach to Interpreting Model Predictions—解释模型预测的统一方法》论文解读与翻译
  5. WSL:WSL(Windows Subsystem for Linux)的简介、安装、使用方法之详细攻略
  6. 成功解决ModuleNotFoundError: No module named 'keras_retinanet.utils.compute_overlap'
  7. DL之CNN:基于CNN-RNN(GRU,2)算法(keras+tensorflow)实现不定长文本识别
  8. Love:程序猿的方式~【情人节520—我爱你】~动画加音效 → 那些年最浪漫的表白(帮你得到你的她)
  9. 【Python】特征选择方法
  10. 『转载』在vs2008(2005)winform中,打开office文档