9

我有一個創建一個串行調度隊列:dispatch_async VS dispatch_sync執行順序

dispatch_queue_t serialQueue = dispatch_queue_create("com.unique.name.queue", DISPATCH_QUEUE_SERIAL); 

我想用這個序列排隊,以確保訪問類線程安全的,同時自動做異步工作並不需要返回到調用線程。

- (void)addObjectToQueue:(id)object 
{ 
    dispatch_async(serialQueue, ^{ 
     // process object and add to queue 
    }); 
} 

- (BOOL)isObjectInQueue:(id)object 
{ 
    __block BOOL returnValue = NO; 
    dispatch_sync(serialQueue, ^{ 
     // work out return value 
    }); 
    return returnValue; 
} 

如果我稱之爲addObjectToQueue:方法,然後立即撥打isObjectInQueue:方法,是他們保證以相同的順序執行,否則會/可以在isObjectInQueue執行第一?

換句話說,dispatch_async與dispatch_sync(立即調度塊)的執行方式是否完全相同,只是它不會阻塞調用線程?

我已經看到類似的問題,兩種方式的答案,所以我正在尋找一個明確的答案,最好支持Apple文檔。

+2

「如果我調用addObjectToQueue:方法,那麼立即調用isObjectInQueue:方法,它們是否保證按照相同的順序執行,或者/是否可以首先執行isObjectInQueue?通過問這個問題你可能的意思是「我們可以在它被添加到隊列之前詢問對象嗎?」。因爲,顯然,如果在同一個線程上調用這兩個方法,它們將被調用。 – krafter

回答

13

它們是否保證以相同的順序執行?

是的。

請問/可以先執行isObjectInQueue嗎?

是的。

兩個答案都是的原因是你應該考慮線程。這可能是你爲什麼首先使用串行隊列的原因。您正在安全地訪問該隊列。

基本上,塊將按照它們放置在串行隊列中的順序執行。這是100%保證。但是,如果多個線程在這個問題上進行攻擊,那麼在另一個線程有機會添加它之前,一個線程可能會首先從隊列中讀取某些內容。

換句話說,dispatch_async與dispatch_sync(立即調度塊)執行完全相同,只是它不會阻塞調用線程?

沒錯。在這兩種情況下,塊都被添加到隊列中。它會立即添加。 dispatch_sync只等待塊返回前完成,而dispatch_async立即返回。

+2

正確;或者,總結一下,串行隊列上的塊將按照添加的順序執行,並且該順序僅在您從單個線程調度dispatch_async()/ dispatch_sync()'時纔是確定性的。如果多線程,你需要訂單,你必須提供一個同步機制。對於'isObjectInQueue:'的返回值,您可能需要一個「新鮮度」指示器,因爲這可能會在併發環境中很快失去有效性。即併發性很難。 – bbum

+0

雖然這個討論是舊的,但我想重振它以澄清一些時刻。 1.「請問isObjectInQueue會先執行嗎?是的。」 Andrew詢問** METHODS **是否可以按照隨機順序調用的隨機順序運行,而不是dispatch_async中的塊。但是: 2.他可能意味着阻止自己。 :)。 但是,如果在串行隊列中添加最後一個塊,可以在另一個塊之前執行一個塊嗎? **「dispathc_async - 在調度隊列中提交異步執行的塊並立即返回。調用此函數總是在塊被提交後立即返回。」** – krafter

2

我想你的問題是,主線程會繼續運行,而dispatch_async仍在執行隊列操作?我認爲它不會,因爲這值得明確提及。如果有什麼事情,我發現這dispatch_async.3這表明這樣的話:

概念,dispatch_sync()是一個方便的包裝圍繞 dispatch_async(),增加了一個信號量的等待 完成塊,並在該塊周圍封裝以表示其完成。

事實上,如果你遵循的源代碼dispatch_async in queue.c你會看到該塊排隊的前景,只有在此之後,執行返回到調用dispatch_async的代碼。因此,如果隊列是串行的,則來自相同線程的dispatch_async後跟dispatch_sync將按順序排列這些塊。

由於dispatch_sync將阻塞,直到塊(以及串行隊列中之前的所有塊)完成執行,那麼您的代碼將是正確的。如果之前添加的對象在隊列中,則isObjectInQueue:將正確報告。

編輯:在多線程環境中我會寫上面的代碼:

- (void)addObjectToQueue:(id)object 
{ 
    dispatch_barrier_async(_queue, ^{ 
     // process object and add to queue 
    }); 
} 

- (BOOL)isObjectInQueue:(id)object 
{ 
    __block BOOL returnValue = NO; 
    dispatch_sync(_queue, ^{ 
     // work out return value 
    }); 
    return returnValue; 
} 

,因爲每個方法的執行可以在有利於另一個線程的任何一點被推遲。

+0

通過閱讀dispatch_barrier_async文檔,如果您使用自己創建的串行隊列,則行爲將與dispatch_async相同。那是對的嗎? – Andrew

+0

是的。上面的代碼假設爲多線程環境使用併發隊列,因爲序列化讀取將是一個毫無意義的障礙。但是,使用串行隊列時,不存在併發執行屏蔽的塊,因此dispatch_barrier_async相當於dispatch_async。 – Jano

+1

'dispatch_barrier_async()'在串行隊列上不做任何事情,因爲根據定義,所有先前的入列塊將在執行barrier的塊時執行。它隻影響併發隊列。 – bbum