具有异步块的串行调度队列

时间:2015-03-03 00:16:44

标签: multithreading cocoa swift asynchronous grand-central-dispatch

是否有理由将块添加到串行调度队列 异步而不是同步

据我所知,一旦前面的任务完成执行,串行调度队列才开始执行队列中的下一个任务。如果是这种情况,我无法通过提交一些块异步来看你会得到什么 - 提交行为可能不会阻止该线程(因为它直接返回),但是任务不会被执行,直到最后一个任务完成,所以在我看来你没有真正获得任何东西。

以下代码提示了这个问题 - 摘自有关设计模式的书籍章节。为了防止底层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);
        });
    }
}

2 个答案:

答案 0 :(得分:3)

因为没有必要让returnToPool()阻止调用者。它可能会继续做其他有用的工作。

调用returnToPool()的线程可能不是只是使用此池。它可能有其他可能正在做的事情。这些东西可以与异步提交的任务中的工作同时完成。

典型的现代计算机具有多个CPU内核,因此这样的设计可以提高CPU核心的有效利用率,并且可以更快地完成有用的工作。问题不在于提交给串行队列的任务是否同时运行 - 由于串行队列的性质,它们不能 - 它是否可以同时完成其他工作。

答案 1 :(得分:1)

是的,有理由将异步添加到串行队列的任务。这实际上非常普遍。

最常见的例子是当您在后台执行某些操作并想要更新UI时。您经常将该UI更新异步调度回主队列(这是一个串行队列)。这样后台线程不必等待主线程执行其UI更新,而是可以在后台进行处理。

另一个常见示例是,当您使用GCD队列同步与某个对象的交互时,您已经演示过。如果您正在处理不可变对象,则可以将这些更新异步调度到此同步队列(即为什么当前线程等待,而是让它继续)。你会同步读取(因为你显然要等到你得到同步值),但写入可以异步完成。

(您实际上看到后一个示例经常使用“reader-writer”模式和自定义并发队列实现,其中读取在与dispatch_sync的并发队列上同步执行,但写入与屏幕异步执行{ {1}}。但这个想法同样适用于串行队列。)

同步v异步调度的选择与目标队列是串行还是并发无关。这只是一个问题,你是否必须阻止当前队列,直到另一个完成任务为止。

关于您的代码示例代码,这是正确的。 dispatch_barrier_async应该同步调度(因为您必须等待同步队列实际返回值),但getFromPool可以安全地异步调度。显然,我担心看到代码等待信号量,如果可能从主线程调用(所以请确保你不要从主线程中调用returnToPool!),但是有一点需要注意,这段代码应该达到预期的目的,提供这个池对象的合理有效的同步,但是getFromPool将阻塞池,如果池是空的,直到将某些内容添加到池中。