日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

Linux环境多线程编程基础设施

發(fā)布時間:2025/7/25 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux环境多线程编程基础设施 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Linux環(huán)境多線程編程基礎設施

本文介紹多線程環(huán)境下并行編程的基礎設施。主要包括:

  • Volatile
  • __thread
  • Memory Barrier
  • __sync_synchronize

volatile

編譯器有時候為了優(yōu)化性能,會將一些變量的值緩存到寄存器中,因此如果編譯器發(fā)現(xiàn)該變量的值沒有改變的話,將從寄存器里讀出該值,這樣可以避免內(nèi)存訪問。

但是這種做法有時候會有問題。如果該變量確實(以某種很難檢測的方式)被修改呢?那豈不是讀到錯的值?是的。在多線程情況下,問題更為突出:當某個線程對一個內(nèi)存單元進行修改后,其他線程如果從寄存器里讀取該變量可能讀到老值,未更新的值,錯誤的值,不新鮮的值。

如何防止這樣錯誤的“優(yōu)化”?方法就是給變量加上volatile修飾。

volatile int i=10;//用volatile修飾變量i ......//something happened int b = i;//強制從內(nèi)存中讀取實時的i的值

OK,畢竟volatile不是完美的,它也在某種程度上限制了優(yōu)化。有時候是不是有這樣的需求:我要你立即實時讀取數(shù)據(jù)的時候,你就訪問內(nèi)存,別優(yōu)化;否則,你該優(yōu)化還是優(yōu)化你的。能做到嗎?

不加volatile修飾,那么就做不到前面一點。加了volatile,后面這一方面就無從談起,怎么辦?傷腦筋。

其實我們可以這樣:

int i = 2; //變量i還是不用加volatile修飾#define ACCESS_ONCE(x) (* (volatile typeof(x) *) &(x))

需要實時讀取i的值時候,就調(diào)用ACCESS_ONCE(i),否則直接使用i即可。

這個技巧,我是從《Is parallel programming hard?》上學到的。

聽起來都很好?然而險象環(huán)生:volatile常被誤用,很多人往往不知道或者忽略它的兩個特點:在C/C++語言里,volatile不保證原子性;使用volatile不應該對它有任何Memory Barrier的期待。

第一點比較好理解,對于第二點,我們來看一個很經(jīng)典的例子:

volatile int is_ready = 0; char message[123]; void thread_A {while(is_ready == 0){}//use message; } void thread_B {strcpy(message,"everything seems ok");is_ready = 1; }

線程B中,雖然is_readyvolatile修飾,但是這里的volatile不提供任何Memory Barrier,因此12行和13行可能被亂序執(zhí)行,is_ready = 1被執(zhí)行,而message還未被正確設置,導致線程A讀到錯誤的值。

這意味著,在多線程中使用volatile需要非常謹慎、小心。

__thread

__threadgcc內(nèi)置的用于多線程編程的基礎設施。用__thread修飾的變量,每個線程都擁有一份實體,相互獨立,互不干擾。舉個例子:

#include<iostream> #include<pthread.h> #include<unistd.h> using namespace std; __thread int i = 1; void* thread1(void* arg); void* thread2(void* arg); int main() {pthread_t pthread1;pthread_t pthread2;pthread_create(&pthread1, NULL, thread1, NULL);pthread_create(&pthread2, NULL, thread2, NULL);pthread_join(pthread1, NULL);pthread_join(pthread2, NULL);return 0; } void* thread1(void* arg) {cout<<++i<<endl;//輸出 2 return NULL; } void* thread2(void* arg) {sleep(1); //等待thread1完成更新cout<<++i<<endl;//輸出 2,而不是3return NULL; }

需要注意的是:

1,__thread可以修飾全局變量、函數(shù)的靜態(tài)變量,但是無法修飾函數(shù)的局部變量。

2,被__thread修飾的變量只能在編譯期初始化,且只能通過常量表達式來初始化。

Memory Barrier

為了優(yōu)化,現(xiàn)代編譯器和CPU可能會亂序執(zhí)行指令。例如:

int a = 1; int b = 2; a = b + 3; b = 10;

CPU亂序執(zhí)行后,第4行語句和第5行語句的執(zhí)行順序可能變?yōu)橄萣=10然后再a=b+3

有些人可能會說,那結果不就不對了嗎?b為10,a為13?可是正確結果應該是a為5啊。

哦,這里說的是語句的執(zhí)行,對應的匯編指令不是簡單的mov b,10和mov b,a+3。

生成的匯編代碼可能是:

movl b(%rip), %eax ; 將b的值暫存入%eax movl $10, b(%rip) ; b = 10 addl $3, %eax ; %eax加3 movl %eax, a(%rip) ; 將%eax也就是b+3的值寫入a,即 a = b + 3

這并不奇怪,為了優(yōu)化性能,有時候確實可以這么做。但是在多線程并行編程中,有時候亂序就會出問題。

一個最典型的例子是用鎖保護臨界區(qū)。如果臨界區(qū)的代碼被拉到加鎖前或者釋放鎖之后執(zhí)行,那么將導致不明確的結果,往往讓人不開心的結果。

還有,比如隨意將讀數(shù)據(jù)和寫數(shù)據(jù)亂序,那么本來是先讀后寫,變成先寫后讀就導致后面讀到了臟的數(shù)據(jù)。因此,Memory Barrier就是用來防止亂序執(zhí)行的。具體說來,Memory Barrier包括三種:

1,acquire barrieracquire barrier之后的指令不能也不會被拉到該acquire barrier之前執(zhí)行。

2,release barrierrelease barrier之前的指令不能也不會被拉到該release barrier之后執(zhí)行。

3,full barrier。以上兩種的合集。

所以,很容易知道,加鎖,也就是lock對應acquire barrier;釋放鎖,也就是unlock對應release barrier。哦,那么full barrier呢?

__sync_synchronize

__sync_synchronize就是一種full barrier

總結

以上是生活随笔為你收集整理的Linux环境多线程编程基础设施的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。