我們知道iOS中的數(shù)組和字典是線程不安全的。多線程編程中局嘁,當(dāng)多個(gè)線程對(duì)同一個(gè)字典dictionary進(jìn)行多讀單寫操作時(shí),該如何來保證數(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;
}
首先悦昵,我們需要?jiǎng)?chuàng)建一個(gè)私有的并行隊(duì)列來處理讀寫操作。
在這里不應(yīng)該使用globe_queue, 因?yàn)槲覀兺ㄟ^dispatch_barrier_async來保證寫操作的互斥晌畅,我們不希望寫操作阻塞住globe_queue中的其他不相關(guān)任務(wù)但指,我們只希望在寫的同時(shí),不會(huì)有其他的寫操作或者讀操作抗楔。
同時(shí)棋凳,也不推薦給隊(duì)列設(shè)置優(yōu)先級(jí),多數(shù)情況下使用default就可以了连躏。而改變優(yōu)先級(jí)往往會(huì)造成一些無法預(yù)料的問題剩岳,比如優(yōu)先級(jí)反轉(zhuǎn)(具體的可以參看參考文獻(xiàn))。
dispatch_barrier_async的block運(yùn)行時(shí)機(jī)是入热,在它之前所有的任務(wù)執(zhí)行完畢拍棕,并且在它后面的任務(wù)開始之前,期間不會(huì)有其他的任務(wù)執(zhí)行勺良。注意在barrier執(zhí)行的時(shí)候绰播,隊(duì)列本質(zhì)上如同一個(gè)串行隊(duì)列尚困,其執(zhí)行完以后才會(huì)恢復(fù)到并行隊(duì)列。
另外一個(gè)值得注意的問題是,在寫操作的時(shí)候滔韵,我們使用dispatch_async,而在讀操作的時(shí)候我們使用dispatch_sync奏属。很明顯潮峦,這2個(gè)操作一個(gè)是異步的囱皿,一個(gè)是同步的忱嘹。我們不需要使每次程序執(zhí)行的時(shí)候都等待寫操作完成,所以寫操作異步執(zhí)行拘悦,但是我們需要同步的執(zhí)行讀操作來保證程序能夠立刻得到它想要的值。(寫操作是異步分苇,不需要等待結(jié)果,直接返回医寿;讀操作是同步蘑斧,需要等待讀結(jié)果,是同步)竖瘾。
使用sync的時(shí)候需要極其的小心,因?yàn)樯圆蛔⒁獠洞陀锌赡墚a(chǎn)生死鎖,這可能造成災(zāi)難性的后果乐横。你肯定也注意到了在寫操作的時(shí)候?qū)ey進(jìn)行了copy, 關(guān)于此處的解釋,插入一段來自參考文獻(xiàn)的引用:
函數(shù)調(diào)用者可以自由傳遞一個(gè)NSMutableString的key葡公,并且能夠在函數(shù)返回后修改它。因此我們必須對(duì)傳入的字符串使用copy操作以確保函數(shù)能夠正確地工作涵亏。如果傳入的字符串不是可變的(也就是正常的NSString類型),調(diào)用copy基本上是個(gè)空操作气筋。