2017-07-25 53 views
0

我讀過幾個職位對原子和寫入演示驗證線程安全的,這樣目標C原子屬性線程安全

@property(atomic,assign) NSInteger sum; 

//然後做這個

for (NSInteger i = 0; i<1000; i++) { 
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT); 
    dispatch_async(queue, ^{ 
     self.sum++; 
    }); 

} 

使財產「總和」作爲原子性質;並啓動1000個併發線程來添加一個;

雖然結果是1000,但它不是,如果我添加一個NSLock來包裝self.sum ++,結果是1000;

有人幫我解釋一下嗎?

+0

你得到什麼結果呢? – rmaddy

+0

爲什麼你在循環內而不是在循環之前創建隊列? – rmaddy

+0

997 998 996不1000,在循環,使1000個線程循環之前 – NickYu

回答

3

這有幾個層次。

首先,聲明的屬性大多隻是聲明存取方法的捷徑。如果不提供自己的實現,則默認情況下發生的編譯器綜合屬性會定義這些方法和實例變量以支持該屬性。

所以,這樣的:

@property(atomic,assign) NSInteger sum; 

基本上只是這樣的:

- (NSInteger) sum; 
- (void) setSum:(NSInteger)value; 

屬性的合成產生的那些方法的一個實例變量和實施方式:

@implementation ... 
{ 
    NSUInteger _sum; 
} 

- (NSInteger) sum 
{ 
    // ... 
} 
- (void) setSum:(NSInteger)value 
{ 
    // ... 
} 

對於原子性質,實現-sum-setSum:各自保證運行,以至於兩者都不會中斷另一方。與-setSum:調用「同時」發生的-sum的調用將返回-setSum:之前的值或之後的值,但不會返回部分修改的frankenstein值或任何臨時值。同樣,同時撥打-setSum:的兩個電話將導致_sum具有來自其中一個或另一個電話的值,但從不會有一些混合或臨時值。這兩個電話似乎是以嚴格的順序發生的,無論是A然後B還是B然後A是任意的。

這對於具有複合類型的房產比較容易理解,如NSRect。設置該屬性的兩個線程永遠不會導致例如來自一個線程的origin和來自另一個線程的size被存儲。一方或另一方將「贏」,並且協議將保持一致。同樣,調用getter的線程也不會看到混合值,即使它與調用者的調用同時發生。

接下來,使用點語法訪問屬性(例如self.sum)實際上只是調用訪問器的快捷方式。因爲只有get和set訪問,並沒有任何「增值」訪問,就像self.sum++;需求的語句來完成這兩個,分別:

[self setSum:[self sum] + 1]; 

所以,你的語句首先涉及到一個呼叫-sum然後到-setSum:通話每個線程。沒有什麼能夠確保其他線程不能彼此交錯操作。該屬性的原子性並不妨礙它。也就是說,線程A可以從它的調用-sum得到值5,線程B也可以從它的調用-sum得到值5,每一個都可以計算6作爲新值,然後他們都稱-setSum:與值6。所以,兩個線程都「增加」的屬性,但它只會增加了1

總之,原子是不是線程安全的。認爲它是一個概念錯誤。這只是原子性。它確實可以防止多線程同時訪問同一個屬性時可能發生的一種腐敗,但不是所有類型的。

+0

很詳細的說明,THX – NickYu

+0

你能在原子屬性添加補充說明,當開發人員定義自己的getter和setter。換句話說,如果我聲明一個原子屬性並提供訪問器實現,那麼我還有一個原子屬性嗎? –

+0

@TimReddy,如果財產申報原子,那麼你有責任確保原子性,當你實現存取。一般情況下,你不能與編譯器生成的訪問使用,以保持原子性,因此,如果您實現一個原子讀寫屬性的訪問的一個機制進行互操作,必須同時實現。如果你的實現無法維護聲明中承諾的原子性,那麼它們就是bug。 –

0

嘗試這樣的:

dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT); 
for (NSInteger i = 0; i<1000; i++) { 
    dispatch_async(queue, ^{ 
     NSLock *aLock = [[NSLock alloc] init]; 
     [aLock lock]; 
     self.sum++; 
     [aLock unlock]; 
    }); 
} 
+0

嘗試仍然不是1000 – NickYu