是否有理由将块添加到串行调度队列 异步而不是同步?
据我所知,一旦前面的任务完成执行,串行调度队列才开始执行队列中的下一个任务。如果是这种情况,我无法通过提交一些块异步来看你会得到什么 - 提交行为可能不会阻止该线程(因为它直接返回),但是任务不会被执行,直到最后一个任务完成,所以在我看来你没有真正获得任何东西。
以下代码提示了这个问题 - 摘自有关设计模式的书籍章节。为了防止底层data
数组被两个独立的线程同时修改,所有修改任务都被添加到串行调度队列中。但请注意,returnToPool
将任务添加到此队列异步,而getFromPool
将其任务同步添加。
class Pool<T> {
private var data = [T]();
// Create a serial dispath queue
private let arrayQ = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_SERIAL);
private let semaphore:dispatch_semaphore_t;
init(items:[T]) {
data.reserveCapacity(data.count);
for item in items {
data.append(item);
}
semaphore = dispatch_semaphore_create(items.count);
}
func getFromPool() -> T? {
var result:T?;
if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) == 0) {
dispatch_sync(arrayQ, {() in
result = self.data.removeAtIndex(0);
})
}
return result;
}
func returnToPool(item:T) {
dispatch_async(arrayQ, {() in
self.data.append(item);
dispatch_semaphore_signal(self.semaphore);
});
}
}
答案 0 :(得分:3)
因为没有必要让returnToPool()
阻止调用者。它可能会继续做其他有用的工作。
调用returnToPool()
的线程可能不是只是使用此池。它可能有其他可能正在做的事情。这些东西可以与异步提交的任务中的工作同时完成。
典型的现代计算机具有多个CPU内核,因此这样的设计可以提高CPU核心的有效利用率,并且可以更快地完成有用的工作。问题不在于提交给串行队列的任务是否同时运行 - 由于串行队列的性质,它们不能 - 它是否可以同时完成其他工作。
答案 1 :(得分:1)
是的,有理由将异步添加到串行队列的任务。这实际上非常普遍。
最常见的例子是当您在后台执行某些操作并想要更新UI时。您经常将该UI更新异步调度回主队列(这是一个串行队列)。这样后台线程不必等待主线程执行其UI更新,而是可以在后台进行处理。
另一个常见示例是,当您使用GCD队列同步与某个对象的交互时,您已经演示过。如果您正在处理不可变对象,则可以将这些更新异步调度到此同步队列(即为什么当前线程等待,而是让它继续)。你会同步读取(因为你显然要等到你得到同步值),但写入可以异步完成。
(您实际上看到后一个示例经常使用“reader-writer”模式和自定义并发队列实现,其中读取在与dispatch_sync
的并发队列上同步执行,但写入与屏幕异步执行{ {1}}。但这个想法同样适用于串行队列。)
同步v异步调度的选择与目标队列是串行还是并发无关。这只是一个问题,你是否必须阻止当前队列,直到另一个完成任务为止。
关于您的代码示例代码,这是正确的。 dispatch_barrier_async
应该同步调度(因为您必须等待同步队列实际返回值),但getFromPool
可以安全地异步调度。显然,我担心看到代码等待信号量,如果可能从主线程调用(所以请确保你不要从主线程中调用returnToPool
!),但是有一点需要注意,这段代码应该达到预期的目的,提供这个池对象的合理有效的同步,但是getFromPool
将阻塞池,如果池是空的,直到将某些内容添加到池中。