多核、多處理器環境下多執行緒同步技巧,來自cocoachina。
我們這裡大部分人應該已經熟悉了在單核單處理器的環境下對多執行緒進行同步的方法。傳統的方法有:mutex(互斥體)、semaphore(訊號 量)、event(事件)、mailbox(郵箱)、message(訊息)等。這些方法都有個共同的特性——即在使用這些方法的前、後會分別加 上關中斷、開中斷操作(或是任務排程的禁止、開啟)。
而在多核情況下單單開關中斷是不起作用的。因為當乙個執行緒在核心0中執行時,它無法一直去關 心核心1中的執行緒執行狀態,它甚至不知道核心1到底在執行哪個執行緒。
因此需要通過另一種方法來解決執行緒同步問題。
這裡比較早的方法是通過原子操作做成旋鎖進行同步。
什麼是原子操作?
原子操作是指當對乙個指定的儲存單元進行這樣的操作時,該操作不會被任何事件打斷; 另一方面,當對乙個指定的儲存單元進行這樣的時,其它外部的訪問都無法對該儲存單元進行訪問,直到該操作完成。
intel在x86架構下 提供xchg指令來實現對儲存器的原子操作:
xchg [memory], register
其功能是將register 的值與指定儲存器單元的值進行叫喚。
下面貼出**示例:
.text
.align 2
.globl _cmpxchg_64
.globl _xchg_64
// extern long xchg_64(register volatile long *pmem, register long reg);
_xchg_64:
xchg %rsi, (%rdi)
mov %rsi, %rax
ret
上面**是乙份at&t的彙編源**,通過.s檔案進行儲存。
下面貼出如何在c原始檔中進行呼叫該函式:
#include
// pmem:rdi reg:rsi
extern long xchg_64(register volatile long *pmem, register long reg);
int main (int argc, const char * argv)
好了。我們有了原子操作後就能做出旋鎖機制來解決多核環境的多執行緒同步問題。
下面通過乙個簡單的**來說明問題:
extern
long xchg_64(
register
volatile
long
*pmem, register
long reg);
int try_get_lock(
volatile
long
*plock)
void release_lock(
volatile
long
*plock)
static
volatile
int counter =
0;static
void threadproc(
void)
這裡先自己規定,鎖的初始值是0,當有執行緒加鎖時,對其置為 1,釋放鎖時再將它置為0。
這裡我們有乙個執行緒例程threadproc,它將會被多個執行緒呼叫。這個例程的功能就是將counter值遞增。當 乙個執行緒進行counter++時,它先取出counter的值,放到暫存器中,然後對該暫存器做加1操作;最後再寫回counter。
如果這裡 沒有旋鎖會發生啥情況呢?
呵呵,將可能會有乙個戲劇性的一幕:比如counter的值為0,然後,當執行緒a將counter值讀入暫存器時,執行緒 b恰恰剛好完成了一次加1操作,此時counter已經變成1了;但是這時,執行緒a中的暫存器值還是原來的0,因此再當執行緒a將counter的值遞增到 1時,它的加1操作等於是失敗了。
而使用旋鎖就不會出問題。因為在將counter的值取出前已經有了鎖保護,當執行緒a獲得鎖之後,mylock 的值會變成1,此時,其它執行緒再呼叫try_get_lock都會返回0,而進行無休止的輪詢。由於這個操作非常快,因此對整體效能而言不會有大的影響。 counter++操作完成後就會立即釋放鎖。
但是為了防止本核由於外部事件而被切出,在必要的場合下還可以新增開關中斷;另外使用旋鎖還有乙個 非常重要的注意事項——當乙個執行緒獲得旋鎖後,在其將鎖釋放之前不能夠將該執行緒銷毀,否則會使其它執行緒處於死鎖狀態。因為鎖得不到釋放,因此其它執行緒都會 陷入無止境的輪詢,呵呵。因此加入開關中斷操作能夠同時也能避免這種情況發生。
上面講述了如何通過旋鎖解決多核、多處理器環境下的多執行緒同步問題。
旋鎖有一些很大的弊端:首先就是可能造成死鎖,除非用開關中斷;另一方面,當 執行緒數量猛增時,鎖的數量也會同時增加,這對於鎖的管理而言會成為很大的軟體管理的負擔。因此這裡將引入一種lock-free的方法來使得一些簡單的操 作不需要通過鎖進行同步。
intel在i386架構下提供了cmpxchg原子操作。該指令的原型如下:
cmpxchg memory, register
功能:將eax(在64位模式下:rax)與memory中的值進行比較,如果相同,zf標誌置1,並且register中的值被載入到memory中;否 則zf標誌位清0,並且將memory中的值載入到register中。
下面將該原子操作封裝成乙個函式,以便c/c++以及 objective-c/c++能夠呼叫:
.text
.align 2
.globl _cmpxchg_64
// cmpdata:rdi loadreg:rsi pmem:rdx
// extern long cmpxchg_64(register long cmpdata, register long *loadreg, register volatile long *pmem);
_cmpxchg_64:
push %rcx
push %rbx
mov %rdi, %rax
mov (%rsi), %rcx
xor %rbx, %rbx
cmpxchg %rcx, (%rdx)
cmovnz %rax, %rcx
mov $1, %rax
cmovz %rax, %rbx
mov %rcx, (%rsi)
mov %rbx, %rax
pop %rbx
pop %rcx
ret
下面我們將用lock-free的方法來改寫threadproc:
#include
extern long cmpxchg_64(register long cmpdata, register long *loadreg, register volatile long *pmem);
static volatile long counter = 0;
static void threadproc(void)
}int main (int argc, const char * argv)
這裡稍微講解一下。
在threadproc中,我們的目的仍然是要將counter加1。那麼我們先讀取counter的值,然後將該值加1後的 值儲存到一邊。然後我們就可以用自己打造的cmpxchg_64做原子操作了。如果返回值為1,說明原來的值與counter在比較時的值是完全一樣的。 比如,初始值為0,那麼在交換前counter的值沒有被其它執行緒改寫,因此仍然為0,這時就可以將加1後的值寫入counter。由於cmpxchg是 乙個原子操作,因此整個比較與交換的過程是原子的,外部不會打斷此操作,並且在此操作期間,匯流排會將counter鎖住,使得其它執行緒要訪問 counter時,其所在的核都會被阻塞。
如果返回0,說明counter被讀取後交換前被修改過,那麼這裡就要重新獲得counter的值並且 在此基礎上做加1操作,然後再執行cmpxchg操作,直到最後成功。
我們可以清楚地看到,這段**中沒有新增任何鎖,而且,如果正在 threadproc的乙個執行緒被銷毀也不會影響其它執行緒的執行,從而我們根本就不需要新增開關中斷的形式來保證避免其它執行緒死鎖的問題。
另外, 大家可以在3樓**中第11行插入counter++;在 看看結果。
最後再提供乙份關於lock-free比較好的資料:non-blocking synchronization
其中,cmpxchg是屬於cas機制;而在armv7架構中的ldrex/strex是屬於 ll/sc機制。它們都可以用來實現lock-free的方法。然而ll/sc機制使用起來必須更加謹慎,因為ll和sc往往必須成對出現,中間也不該允 許被中止。所以這個機制大多架構中用的比較少。現在armv7以及power架構有這個指令集,而armv7只有這一種方法能實現 lock-free。而armv7之前,只能使用swp指令來實現旋鎖,swp與xchg功能完全一樣。
多處理器結構
1 共享儲存器的多處理器 2 並行處理存在的挑戰問題 根據所包含的處理器的數量,可以將現有的共享儲存器的多處理器分為兩類,而處理器的數量又決定了儲存器的組織方式和互聯策略。按照儲存器的組織方式對多處理器進行命名。對稱 共享儲存器 多處理器 smp 又稱集中式共享儲存器多處理器,有時也稱一致儲存器訪問...
多處理器與查詢效能
同乙個資料庫檔案,分別在兩台機器上還原,為什麼執行儲存過程速度不一樣 a機器 xp,mssql 2000,ntfs格式盤,ibm r51e本本,512記憶體 b機器 win2000 mssql 2000,fat32格式盤.在我自已的機器上即a機器,執行時間25秒 在b機器上執行時間為,6分52秒。已...
關於處理器的多核多執行緒
cpu的多核是指cpu的處理器核心數量 cpu的多執行緒是指同乙個處理器上的多個執行緒同步執行並共享處理器的執行資源的執行緒數量 處理器核心 core 又稱為核心,是cpu最重要的組成部分。cpu中心那塊隆起的晶元就是核心,是由單晶矽以一定的生產工藝製造出來的,cpu所有的計算 接受 儲存命令 處理...