2
//my_serial_queue is a serial_dispatch_queue 

dispatch_async(my_serial_queue, ^{ 

    //access a shared resource such as a bank account balance 
    [self changeBankAccountBalance]; 

}); 

如果我提交100個任務,每個訪問和變更銀行賬戶餘額,我明白一個串行隊列將按順序執行每個任務,但這些任務也按順序完成,以及使用dispatch_async時?串行調度隊列如何保證資源保護?

如果我異步提交給串行隊列的任務#23需要很長時間才能完成?任務#24是否僅在任務#23完成時啓動,或者任務#24在任務#23完成之前開始?如果是這樣,任務#24在開始工作時是否有錯誤的銀行賬戶餘額,從而破壞了數據的完整性?

謝謝!

+5

看完這個問題後,標題應該是:串行調度隊列是否保證串行執行?答案是肯定的,因爲串行執行是隊列的屬性,而不是dispatch_ [async | sync]函數的屬性。 – Jano

+0

Jano謝謝。我將調度異步與隊列屬性混爲一談,這是兩個不同的想法。對原始標題中的愚蠢單詞選擇抱歉。我已經修復了標題 – user798719

回答

4

man dispatch_queue_create說:「調度到串行隊列的塊所執行的所有內存寫入都將保證對分派到同一隊列的後續塊可見。」因此,串行隊列是將訪問權限序列化爲可變狀態的好方法避免比賽條件。

當使用dispatch_async時,這些任務是否按順序完成呢?

是的。隊列決定了執行策略,而不是如何對塊進行排隊。

換句話說,如果隊列是串行的,則使用異步或同步進行排隊不會改變該隊列。唯一的區別是:在繼續執行程序的其餘部分之前是否等待此塊完成? dispatch_async =否,dispatch_sync =是。

如果任務#23異步提交到串行隊列 需要很長時間才能完成?

沒有什麼變化。一個串行隊列總是在出列並執行下一個塊(#24)之前等待先前出列的塊(#23)完成。如果拖延隊列是一個問題,你應該在你的代碼塊中實現一個超時。

7

是的,專用的串行隊列是一種很好的方式來同步訪問多個線程之間共享的資源。而且,對於串行隊列,每個任務都將等待前一個任務完成。

兩個意見:

  1. 雖然這聽起來非常低效的過程,這是隱含在任何同步技術的心臟(基於隊列的或鎖爲基礎的方法是否),其目標是最小化併發共享資源的更新。

    但是在很多情況下,串行隊列技術可以比其他常用技術(例如簡單互斥鎖,NSLock@synchronized指令)產生明顯更好的性能。有關其他同步技術的討論,請參閱「線程編程指南」的Synchronization部分。有關使用隊列來代替鎖的討論,請參閱並行編程指南中的從線程部分移除Eliminating Lock-Based Code

  2. 串行隊列模式的變化是使用了「讀寫器」的模式,在那裏你創建一個GCD併發隊列:

    queue = dispatch_queue_create("identifier", DISPATCH_QUEUE_CONCURRENT); 
    

    然後進行讀取使用dispatch_sync,但在執行寫入使用dispatch_barrier_async。淨效率是允許併發讀取操作,但確保寫入不會同時執行。

    如果您的資源允許併發讀取,那麼讀寫器模式可以提供比串行隊列更高的性能增益。

因此,簡而言之,而它似乎效率不高有任務#24等待任務#23,這是在你努力減少共享資源的併發更新任何同步技術所固有的。 GCD串行隊列是一個令人驚訝的高效機制,通常比許多簡單的鎖定機制更好。在某些情況下,讀寫器模式可以進一步提高性能。


我原來的答案,下面,是爲了響應原來的問題這混淆了題爲「如何做一個串行調度隊列保證併發?」回想起來,這只是一個意外的錯誤用法。


這是一個有趣的選擇,「串行調度隊列如何保證併發性?」

還有three types of queues,串口,併發和主隊列。顧名思義,串行隊列將不會啓動下一個調度塊,直到前一個完成。 (使用你的例子,這意味着如果任務23需要很長時間,它將不會開始任務24,直到它完成。)有時這是關鍵的(例如,如果任務24取決於任務23的結果或者如果任務23和24個試圖訪問相同的共享資源)。

如果你想這些不同的派遣任務,相對於同時運行到對方,您可以使用併發隊列(無論您通過dispatch_get_global_queue得到全局併發隊列中的一個,也可以創建使用dispatch_queue_create用自己的併發隊列DISPATCH_QUEUE_CONCURRENT選項)。在併發隊列中,許多分派的任務可能會同時運行。使用併發隊列需要注意一些問題(特別是共享資源的synchronization),但在正確實施時可以產生顯着的性能優勢。

作爲這兩種方法之間的折中方案,您可以使用操作隊列,它可以是併發的,但也可以通過設置maxConcurrentOperationCount來限制隊列中有多少操作將同時運行。您將使用這種典型場景是在執行後臺網絡任務時,您不需要超過五個併發網絡請求。

欲瞭解更多信息,請參閱Concurrency Programming Guide

+0

很好的答案。只需添加:NSOperationQueue/NSOperation還允許您設置依賴關係,以便即使在併發隊列中,某些操作也會等待以前的操作完成。 (這也可以用調度隊列,但稍微簡單一點。) –

+0

@AndrewMadsen同意。我不想深入瞭解調度隊列和操作隊列之間的所有區別(這在文檔中有廣泛的介紹)。坦率地說,取消操作隊列中的操作的能力是另一個巨大的差異。 GCD也有自己的優勢。我只是專注於串行/併發維度(尤其是考慮到問題標題措辭的有趣選擇)。 – Rob