亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

蟲蟲首頁| 資源下載| 資源專輯| 精品軟件
登錄| 注冊

您現在的位置是:首頁 > 技術閱讀 >  多線程程序中操作的原子性

多線程程序中操作的原子性

時間:2024-02-10
來源:Guancheng (G.C.)

0. 背景


原子操作就是不可再分的操作。在多線程程序中原子操作是一個非常重要的概念,它常常用來實現一些同步機制,同時也是一些常見的多線程Bug的源頭。

本文主要討論了三個問題:

1. 多線程程序中對變量的讀寫操作是否是原子的?

2. 多線程程序中對Bit field(位域)的讀寫操作是否是線程安全的?

3. 程序員該如何使用原子操作?


我們先從一道很熱門的百度筆試題講起。很多人講不清楚其背后的原理,下面我們就來對它進行一下剖析(其實這個題目有點歧義,后面我們會講到):

以下多線程對int型變量x的操作,哪幾個需要進行同步:( )
A. x=y; B. x++; C. ++x; D. x=1;

要徹底理解這個問題,我們首先需要從硬件講起。以常見的X86 CPU來說,根據Intel的參考手冊,它基于以下三種機制保證了多核中加鎖的原子操作(8.1節):

(1)Guaranteed atomic operations (注:8.1.1節有詳細介紹)
(2)Bus locking, using the LOCK# signal and the LOCK instruction prefix
(3)Cache coherency protocols that ensure that atomic operations can be carried out on cached data structures (cache lock); this mechanism is present in the Pentium 4, Intel Xeon, and P6 family processors


這三個機制相互獨立,相輔相承。簡單的理解起來就是:

(1)一些基本的內存讀寫操作是本身已經被硬件提供了原子性保證(例如讀寫單個字節的操作);
(2)一些需要保證原子性但是沒有被第(1)條機制提供支持的操作(例如read-modify-write)可以通過使用”LOCK#”來鎖定總線,從而保證操作的原子性
(3)因為很多內存數據是已經存放在L1/L2 cache中了,對這些數據的原子操作只需要與本地的cache打交道,而不需要與總線打交道,所以CPU就提供了cache coherency機制來保證其它的那些也cache了這些數據的processor能讀到最新的值。


那么CPU對哪些(1)中的基本的操作提供了原子性支持呢?根據Intel手冊8.1.1節的介紹:

從Intel486 processor開始,以下的基本內存操作是原子的:


? Reading or writing a byte(一個字節的讀寫)
? Reading or writing a word aligned on a 16-bit boundary(對齊到16位邊界的字的讀寫)
? Reading or writing a doubleword aligned on a 32-bit boundary(對齊到32位邊界的雙字的讀寫)

從Pentium processor開始,除了之前支持的原子操作外又新增了以下原子操作:

? Reading or writing a quadword aligned on a 64-bit boundary(對齊到64位邊界的四字的讀寫)
? 16-bit accesses to uncached memory locations that fit within a 32-bit data bus(未緩存且在32位數據總線范圍之內的內存地址的訪問)

從P6 family processors開始,除了之前支持的原子操作又新增了以下原子操作:

? Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line(對單個cache line中緩存地址的未對齊的16/32/64位訪問)

需要注意的是盡管從P6 family開始對一些非對齊的讀寫操作已經提供了原子性保障,但是非對齊訪問是非常影響性能的,需要盡量避免。當然了,對于一般的程序員來說不需要太擔心這個,因為大部分編譯器會自動幫你完成內存對齊。



回到最開始那個筆試題。我們先反匯編一下看看它們到底執行了什么操作:


x = y;mov eax,dword ptr [y]mov dword ptr [x],eax
x++;mov eax,dword ptr [x]add eax,1mov dword ptr [x],eax
++x;mov eax,dword ptr [x]add eax,1mov dword ptr [x],eax
x = 1;mov dword ptr [x],1


(1)很顯然,x=1是原子操作。


因為x是int類型,32位CPU上int占32位,在X86上由硬件直接提供了原子性支持。實際上不管有多少個線程同時執行類似x=1這樣的賦值語句,x的值最終還是被賦的值(而不會出現例如某個線程只更新了x的低16位然后被阻塞,另一個線程緊接著又更新了x的低24位然后又被阻塞,從而出現x的值被損壞了的情況)。


(2)再來看x++和++x。


其實類似x++, x+=2, ++x這樣的操作在多線程環境下是需要同步的。因為X86會按三條指令的形式來處理這種語句:從內存中讀x的值到寄存器中,對寄存器加1,再把新值寫回x所處的內存地址(見上面的反匯編代碼)。


例如有兩個線程,它們按照如下順序執行(注意讀x和寫回x是原子操作,兩個線程不能同時執行):


time    Thread 1         Thread 20      load eax, x1                            load eax, x2      add eax, 1        add eax, 13      store x, eax4                            store x, eax


我們會發現最終x的值會是1而不是2,因為Thread 1的結果被覆蓋掉了。這種情況下我們就需要對x++這樣的操作加鎖(例如Pthread中的mutex)以保證同步,或者使用一些提供了atomic operations的庫(例如Windows API中的atomic庫,Linux內核中的atomic.h,Java concurrent庫中的Atomic Integer,C++0x中即將支持的atomic_int等等,這些庫會利用CPU提供的硬件機制做一層封裝,提供一些保證了原子性的API)。


(3)最后來看看x=y。


在X86上它包含兩個操作:讀取y至寄存器,再把該值寫入x。讀y的值這個操作本身是原子的,把值寫入x也是原子的,但是兩者合起來是不是原子操作呢?我個人認為x=y不是原子操作,因為它不是不可再分的操作。但是它需要不需要同步呢?其實問題的關鍵在于程序的上下文。


例如有兩個線程,線程1要執行{y = 1; x = y;},線程2要執行{y = 2; y = 3;},假設它們按如下時間順序執行:


time    Thread 1        Thread 20        store y, 11                            store y, 22        load eax, y3                            store y, 34        store x, eax


那么最終線程1中x的值為2,而不是它原本想要的1。我們需要加上相應的同步語句確保y = 2不會在線程1的兩條語句之間發生。y = 3那條語句盡管在load y和store x之間執行,但是卻不影響x=y這條語句本身的語義。所以你可以說x=y需要同步,也可以說x=y不需要同步,看你怎么理解題意了。x=1是否需要同步也是一樣的道理,雖然它本身是原子操作,但是如果有另一個線程要讀x=1之后的值,那肯定也需要同步,否則另一個線程讀到的就是x的舊值而不是1了。


2. 對Bit field(位域)的讀寫操作是否是線程安全的?


Bit field常用來高效的存儲有限位數的變量,多用于內核/底層開發中。一般來說,對同一個結構體內的不同bit成員的多線程訪問是無法保證線程安全的。


例如Wikipedia中的如下例子:


struct foo {    int flag : 1;    int counter : 15;};
struct foo my_foo;
/* ... */
/* in thread 1 */
pthread_mutex_lock(&my_mutex_for_flag);my_foo.flag = !my_foo.flag;pthread_mutex_unlock(&my_mutex_for_flag);
/* in thread 2 */
pthread_mutex_lock(&my_mutex_for_counter);++my_foo.counter;pthread_mutex_unlock(&my_mutex_for_counter);


兩個線程分別對my_foo.flag和my_foo.counter進行讀寫操作,但是即使有上面的加鎖方式仍然不能保證它是線程安全的。原因在于不同的成員在內存中的具體排列方式“跟Byte Order、Bit Order、對齊等問題都有關,不同的平臺和編譯器可能會排列得很不一樣,要編寫可移植的代碼就不能假定Bit-field是按某一種固定方式排列的”[3]。而且一般來講CPU對內存操作的最小單位是word(X86的word是16bits),而不是1bit。這就是說,如果my_foo.flag和my_foo.counter存儲在同一個word里,CPU在讀寫任何一個bit member的時候會同時把兩個值一起讀進寄存器,從而造成讀寫沖突。這個例子正確的處理方式是用一個mutex同時保護my_foo.flag和my_foo.counter,這樣才能確保讀寫是線程安全的。


在C++0x草案中對bit field是這樣定義的:

連續的多個非0bit的bit fields是屬于同一個memory location的;長度為0bit的bit field會把占單獨的一個memory location。對同一個memory location的讀寫不是線程安全的;對不同memory location的讀寫是線程安全的。



這里有一個因為Bit field不是線程安全所導致的一個Linux內核中的Bug。


引用一下Pongba的總結


所以,如果你的多個bitfields是連續的,同時又想要無沖突的讀取它們,有兩種做法,一是在中間用0大小bitfield隔開,但這種做法實際上就消除了bitfield的節省內存的初衷,因為為了使它們不沖突,至少被隔開的兩個bitfield肯定不可能共享byte了。另一種做法當然就是用鎖了。

3. 程序員該怎么用Atomic操作?


一般情況下程序員不需要跟CPU提供的原子操作直接打交道,所以只需要選擇語言或者平臺提供的atomic API即可。而且使用封裝好了的API還有一個好處是它們常常還提供了諸如compare_and_swap,fetch_and_add這樣既有讀又有寫的較復雜操作的封裝。


常見的API如下:

Windows上InterlockedXXXX的API
GNU/Linux上linux kernel中
atomic_32.h
GCC中的Atomic Builtins (__sync_fetch_and_add()等)
Java中的java.util.concurrent.atomic
C++0x中的
atomic operation
Intel TBB中的atomic operation


4. 參考文獻:


[1] 關于變量操作的原子性(atomicity)FAQ
[2] http://en.wikipedia.org/wiki/Atomic_operation
[3] 關于內存對齊、bit field等 –《Linux C編程一站式學習》
[4] Do you need mutex to protect an ‘int’?
[5] C++ Concurrency in Action
[6] Multithreaded simple data type access and atomic variables



往期推薦



喵哥吐血整理:軟件開發的51條建議

函數返回值的行業潛規則

面試常問的16個C語言問題,你能答上來幾個?

模版定義一定要寫在頭文件中嗎?

四萬字長文,這是我見過最好的模板元編程文章!

C++為什么要弄出虛表這個東西?

研究了一波RTTI,再介紹軟件開發的201個原則,文末再送6本書

【性能優化】高效內存池的設計與實現

亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频
亚洲欧美日韩综合国产aⅴ| 亚洲精品中文字幕在线| 欧美—级高清免费播放| 免费观看成人网| 亚洲高清一区二| 久久精品国产免费| 在线不卡中文字幕| 久久久久久久久伊人| 国产精品视频成人| 久久久精品日韩| 一色屋精品视频在线看| 欧美人与性禽动交情品| 亚洲色图在线视频| 国产精品羞羞答答| 欧美大片在线观看| 一本久道久久综合中文字幕| 国产情侣一区| 久久视频国产精品免费视频在线| 国内精品久久久| 欧美日韩一区二区在线播放| 亚洲午夜一级| 亚洲国产欧美日韩另类综合| 欧美成人久久| 亚洲精品久久久一区二区三区| 欧美午夜片在线观看| 午夜精品福利电影| 日韩视频在线一区| 国产精品美女主播| 亚洲欧美国产日韩天堂区| 亚洲区中文字幕| 国产精品福利久久久| 麻豆精品视频在线观看| 9色porny自拍视频一区二区| 欧美日韩在线视频观看| 另类图片国产| 在线视频欧美精品| 亚洲精品一线二线三线无人区| 欧美性色综合| 国产欧美高清| 国产精品日韩| 母乳一区在线观看| 久久精品国产亚洲精品| 亚洲国产乱码最新视频| 亚洲特级毛片| 欧美激情小视频| 亚洲专区在线| 亚洲欧美激情四射在线日| 一区二区三区在线观看欧美| 欧美午夜精品一区| 欧美1区免费| 老司机免费视频久久| 羞羞答答国产精品www一本| 国产精品久久福利| 亚洲精品一区二区三区福利| 国产日韩在线看| 欧美日本一区| 久久夜色精品国产| 欧美成人精品1314www| 先锋影音久久| 欧美在线精品免播放器视频| 9久re热视频在线精品| 国产精品拍天天在线| 国产精品女同互慰在线看| 欧美国产91| 欧美日韩视频在线| 欧美国产激情二区三区| 欧美精品麻豆| 欧美精品一卡二卡| 欧美精品入口| 欧美激情精品久久久| 久久婷婷色综合| 久久综合色一综合色88| 亚洲系列中文字幕| 99精品国产高清一区二区| 亚洲黄页视频免费观看| 亚洲第一视频| 国产老肥熟一区二区三区| 国产精品影音先锋| 国产伦精品一区二区三区| 国产又爽又黄的激情精品视频 | 国产欧美一区二区三区国产幕精品 | 欧美日韩99| 欧美日韩中文字幕精品| 欧美精品一区二区三区在线播放| 欧美午夜精品久久久久久人妖| 欧美日本韩国一区二区三区| 欧美寡妇偷汉性猛交| 国产精品视频最多的网站| 国产精品日韩欧美一区| 在线观看欧美精品| 最新国产の精品合集bt伙计| 99re亚洲国产精品| 亚洲欧美日韩综合aⅴ视频| 亚洲综合视频一区| 免费精品视频| 欧美日韩在线一二三| 国内精品久久久久久久果冻传媒| 韩国成人理伦片免费播放| 美女视频黄免费的久久| 国产精品亚洲一区二区三区在线| 国产欧美日韩视频一区二区三区 | 欧美一区三区三区高中清蜜桃| 亚洲欧美日本国产专区一区| 嫩模写真一区二区三区三州| 欧美极品在线观看| 国产精品黄页免费高清在线观看| 国产精品女人网站| 国产日韩欧美二区| 一区二区免费在线播放| 亚洲综合成人在线| 欧美破处大片在线视频| 国产精品自拍在线| 一区二区三区欧美亚洲| 小处雏高清一区二区三区| 亚洲制服av| 欧美精品一区二区三| 国产欧美精品一区二区三区介绍| 亚洲精品视频在线播放| 欧美一区二区三区免费大片| 欧美三级午夜理伦三级中文幕| 国产视频一区在线| 99国产精品国产精品毛片| 免费中文日韩| 国产日韩精品一区二区三区在线 | 国产免费观看久久| 亚洲激精日韩激精欧美精品| 午夜精品久久久久久| 国产精品国产a| 亚洲第一精品福利| 久久女同精品一区二区| 国产精品久久久一区二区| 99国产精品久久久| 久久亚洲免费| 国产欧美日韩视频在线观看| 亚洲一区在线免费观看| 欧美激情免费观看| 日韩一二在线观看| 久久综合色婷婷| 亚洲第一在线| 久久精品伊人| 国产美女扒开尿口久久久| 中文高清一区| 欧美多人爱爱视频网站| 亚洲欧洲日韩女同| 久久亚洲图片| 日韩香蕉视频| 欧美国产一区二区在线观看| 国产精品久久久久7777婷婷| 亚洲最新视频在线播放| 欧美成人自拍视频| 在线播放中文字幕一区| 久久精品一区二区三区不卡| 国产精品欧美日韩| 亚洲视频福利| 欧美三区在线| 9色国产精品| 国产精品一区二区久久| 亚洲一本大道在线| 国产精品久久久999| 精品成人一区二区三区四区| 激情成人中文字幕| 欧美性猛交一区二区三区精品| 久久av在线看| 午夜一区二区三区在线观看| 亚洲人成欧美中文字幕| 久热国产精品| 欧美精品一区二区久久婷婷 | 国产欧美日韩免费| 在线不卡视频| 国产亚洲aⅴaaaaaa毛片| 欧美中文在线观看| 影音先锋日韩资源| 亚洲一区在线免费| 伊人久久男人天堂| 欧美中文字幕视频| 在线视频日韩| 欧美精品一区视频| 欧美精品18+| 国产精品免费看片| 亚洲午夜免费视频| 在线不卡免费欧美| 欧美精品一区二区精品网| 香蕉av777xxx色综合一区| 国产日韩综合一区二区性色av| 一区精品久久| 精久久久久久| 日韩视频精品| 久久激情视频免费观看| 亚洲美女淫视频| 国产精品免费观看在线| 欧美承认网站| 新67194成人永久网站| 日韩一级在线| 国产一二三精品| 国产精品视频xxxx| 欧美激情精品久久久| 裸体一区二区三区| 午夜久久资源| 国内久久精品视频| 国产精品久久久久久久午夜|