是的,专用串行队列是同步访问多个线程之间共享的某些资源的绝妙方法。而且,是的,使用串行队列,每个任务都将等待前一个任务完成。
两个观察:
虽然这听起来像是一个非常低效的过程,但它隐含地处于任何同步技术(无论是基于队列还是基于锁的方法)的核心,其目标是最小化共享资源的并发更新。
但在许多情况下,串行队列技术可以产生比其他常见技术(例如简单互斥锁NSLock
、 或@synchronized
指令)更好的性能。有关替代同步技术的讨论,请参阅线程编程指南的同步部分。有关使用队列代替锁的讨论,请参阅并发编程指南的从线程迁移部分中的消除基于锁的代码。
串行队列模式的一种变体是使用“读写器”模式,您可以在其中创建 GCD 并发队列:
queue = dispatch_queue_create("identifier", DISPATCH_QUEUE_CONCURRENT);
然后,您使用 执行读取dispatch_sync
,但使用 执行写入dispatch_barrier_async
。净有效是允许并发读取操作,但确保永远不会同时执行写入。
如果您的资源允许并发读取,那么读写器模式可以提供比串行队列更多的性能增益。
因此,简而言之,虽然让任务 #24 等待任务 #23 似乎效率低下,但这是任何同步技术所固有的,在这种技术中,您努力最小化共享资源的并发更新。而且 GCD 串行队列是一种非常有效的机制,通常比许多简单的锁定机制要好。在某些情况下,读写器模式可以提供进一步的性能改进。
下面我的原始答案是对原始问题的回应,该问题令人困惑,标题为“串行调度队列如何保证并发性?” 回想起来,这只是偶然使用了错误的术语。
这是一个有趣的词语选择,“串行调度队列如何保证并发?”
队列分为三种类型,串行队列、并发队列和主队列。顾名思义,串行队列将在前一个完成之前不会开始下一个调度的块。(使用您的示例,这意味着如果任务 23 需要很长时间,则在完成之前它不会启动任务 24。)有时这很关键(例如,如果任务 24 取决于任务 23 的结果,或者如果两个任务 23和 24 正在尝试访问相同的共享资源)。
如果您希望这些不同的分派任务彼此同时运行,您可以使用并发队列(通过 获得的全局并发队列之一,或者您可以使用选项dispatch_get_global_queue
创建自己的并发队列)。在并发队列中,您的许多分派任务可能同时运行。使用并发队列需要一些注意(特别是共享资源的同步),但如果实施得当,可以产生显着的性能优势。dispatch_queue_create
DISPATCH_QUEUE_CONCURRENT
作为这两种方法的折衷方案,您可以使用操作队列,它可以是并发的,但您也可以通过设置来限制队列上的操作在任何给定时间并发运行的数量maxConcurrentOperationCount
。您将使用它的典型场景是在执行后台网络任务时,您不希望超过五个并发网络请求。
有关详细信息,请参阅并发编程指南。