2014-09-02 63 views
4

我已在下面的代碼段:訪問來自兩個線程變量用C

int attempts = 0; 
while(ptr== NULL && attempts < 60) { 
     sleep(1000); 
     attempts++; 
    } 

連續環路等待指針由另一個線程來設置。另一個線程只會做

ptr = //some value 

我的問題是,這是安全的嗎?這會導致任何內存損壞,導致以後很難調試錯誤條件?

P.S:我知道由於缺少volatile關鍵字,編譯器可能會優化ptr。這對我來說並不重要。我只關心這是否會給代碼的其他不相關部分帶來問題。

+0

考慮使用此參考文獻中描述的原子操作http://en.cppreference.com/w/c/atomic它是在C11規範中引入的。 'atomic_compare_exchange_strong'是你在循環中需要的。 – bkausbk 2014-09-02 09:04:16

+0

看起來很安全,賦值操作是原子的。但是,這種方法並不理智,如果在你睡覺的時候,ptr會被改變成某個值,然後又變回NULL。 – 2014-09-02 09:09:41

+0

另一個線程只會執行 - 只有一個寫入線程?如果是的話,它似乎是安全的,因爲它是簡單的彙編指令(x86上的mov)。如果不是,那麼這是不安全的。 – someuser 2014-09-02 09:19:01

回答

2

至少,ptr可以聲明爲volatile;但那就是not夠了。你真的想要atomic操作。用C11你有<stdatomic.h>標準頭。 最近的海灣合作委員會有atomic builtins你應該使用。您的ptr的訪問和寫入(在「您的其他線程」中)應該是原子的!

實際上(不使用原子操作),您會觀察到的行爲是undefined behavior,並且可能會有很大差異(使用不同的處理器,不同的編譯器等)。

可悲的是,許多x86處理器,你可能沒有注意到,UB

您需要編譯器執行/通過發射特定的機器指令使用cache coherence

你也可以使用帶互斥鎖的condition variables,或者一些semaphore

最近的GCC(至少4.9),如果你的目標處理器支持,你可以考慮編譯-fsanitize=thread和/或-fsanitize=address(用於調試)。

順便說一句,你的內存損壞可能是完全無關的。你可以考慮使用在many platforms上支持的valgrind(最好用-g來編譯你的程序,如果你願意的話,你可以試着編譯gcc -O1 -g)。

我還建議使用最新的工具(GCC最新的4.9版本-in月2014-,新版本binutils,最近GDB,最近的libc,最近的內核....)

+1

沒有太多的答案或解釋(但它是一個很好的選擇)。請詳細說明。 OP需要理解爲什麼。 – 2014-09-02 08:54:37

+0

我的問題是代碼是否會導致任何未定義的行爲。由於缺乏volatile關鍵字,編譯器可能優化ptr的事實對我來說並不重要。 – rubndsouza 2014-09-02 08:57:56

+0

是的,你的代碼會導致UB,但是在大多數x86機器上你可能不會注意到它...... – 2014-09-02 08:59:03

0

據我所知,沒有。這是不安全的,可能會導致讀取不一致。最好的方法是用互斥或二進制信號保護變量ptr。檢查互斥函數的mutex.h(例如mutex_init,mutex_lock,mutex_unlock等)。

3

這個問題根本不可能回答。 C語言不支持線程。所以如果你有線程,你會從一些標準或庫中獲得支持,這些標準或庫需要記錄什麼是安全的,哪些不是。最有可能的是,線程標準說修改一個線程上的一個對象,而另一個線程訪問它可能是未定義的行爲。在這種情況下,這絕對不安全。但是你需要檢查你編碼的特定標準。

順便說一下,您無法通過測試來確定它是否安全。它可能看起來很安全,或者看起來像你在測試中所期望的那樣,然後以不同的操作系統版本,不同的CPU,不同的編譯器選項,不同的BIOS設置,甚至僅僅是由於運氣不佳而失敗。