我們知道iOS中的數(shù)組和字典是線程不安全的。多線程編程中,當(dāng)多個線程對同一個字典dictionary進(jìn)行多讀單寫操作時,該如何來保證數(shù)據(jù)安全呢?
_ioQueue = dispatch_queue_create("ioQueue", DISPATCH_QUEUE_CONCURRENT);
(void)setSafeObject:(id)object forKey:(NSString *)key
{
key = [key copy];
dispatch_barrier_async(self.ioQueue, ^{
if (key && object) {
[_dic setObject:object forKey:key];
}
});
}
(id)getSafeObjectForKey:(NSString *)key
{
__block id result = nil;
dispatch_sync(self.ioQueue, ^{
result = [_dic objectForKey:key];
});
return result;
}
首先,我們需要創(chuàng)建一個私有的并行隊列來處理讀寫操作。
在這里不應(yīng)該使用globe_queue, 因為我們通過dispatch_barrier_async來保證寫操作的互斥,我們不希望寫操作阻塞住globe_queue中的其他不相關(guān)任務(wù),我們只希望在寫的同時,不會有其他的寫操作或者讀操作。
同時,也不推薦給隊列設(shè)置優(yōu)先級,多數(shù)情況下使用default就可以了。而改變優(yōu)先級往往會造成一些無法預(yù)料的問題,比如優(yōu)先級反轉(zhuǎn)(具體的可以參看參考文獻(xiàn))。
dispatch_barrier_async的block運(yùn)行時機(jī)是,在它之前所有的任務(wù)執(zhí)行完畢,并且在它后面的任務(wù)開始之前,期間不會有其他的任務(wù)執(zhí)行。注意在barrier執(zhí)行的時候,隊列本質(zhì)上如同一個串行隊列,其執(zhí)行完以后才會恢復(fù)到并行隊列。
另外一個值得注意的問題是,在寫操作的時候,我們使用dispatch_async,而在讀操作的時候我們使用dispatch_sync。很明顯,這2個操作一個是異步的,一個是同步的。我們不需要使每次程序執(zhí)行的時候都等待寫操作完成,所以寫操作異步執(zhí)行,但是我們需要同步的執(zhí)行讀操作來保證程序能夠立刻得到它想要的值。(寫操作是異步,不需要等待結(jié)果,直接返回;讀操作是同步,需要等待讀結(jié)果,是同步)。
使用sync的時候需要極其的小心,因為稍不注意,就有可能產(chǎn)生死鎖,這可能造成災(zāi)難性的后果。你肯定也注意到了在寫操作的時候?qū)ey進(jìn)行了copy, 關(guān)于此處的解釋,插入一段來自參考文獻(xiàn)的引用:
函數(shù)調(diào)用者可以自由傳遞一個NSMutableString的key,并且能夠在函數(shù)返回后修改它。因此我們必須對傳入的字符串使用copy操作以確保函數(shù)能夠正確地工作。如果傳入的字符串不是可變的(也就是正常的NSString類型),調(diào)用copy基本上是個空操作。