日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(67)多核同步,lock 总线锁 ,自己实现临界区

發布時間:2025/3/21 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (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; } 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的(67)多核同步,lock 总线锁 ,自己实现临界区的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。