//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在启动其工作时是否有错误的银行账户余额,从而搞砸数据完整性?
谢谢!
答案 0 :(得分:7)
是的,专用串行队列是同步访问多个线程之间共享的某些资源的绝佳方式。并且,是的,使用串行队列,每个任务都将等待前一个任务完成。
两个观察结果:
虽然这听起来像是一个非常低效的过程,但这隐含在任何同步技术(无论是基于队列还是基于锁的方法)的核心,其目标是最小化共享资源的并发更新。
但在许多情况下,串行队列技术可以产生比其他常用技术(例如简单的互斥锁,NSLock
或@synchronized
指令)更好的性能。有关备用同步技术的讨论,请参阅线程编程指南的Synchronization部分。有关使用队列代替锁的讨论,请参阅中的Eliminating Lock-Based Code。 并发编程指南中的迁移线程部分。
串行队列模式的一种变体是使用“读写器”模式,您可以在其中创建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共享资源),但在正确实施时可以产生显着的性能优势。
作为这两种方法之间的折衷方案,您可以使用操作队列,它们既可以是并发的,也可以通过设置{{{}来限制队列中的任何操作在任何给定时间同时运行的数量。 1}}。您将使用此功能的典型方案是在执行后台网络任务时,您不需要超过五个并发网络请求。
有关详细信息,请参阅Concurrency Programming Guide。
答案 1 :(得分:4)
man dispatch_queue_create
说:“分派到串行队列的块执行的所有内存写入都保证对分派到同一队列的后续块可见。”因此,串行队列是序列化访问权限的好方法。可变状态以避免竞争条件。
使用dispatch_async时,这些任务也会按顺序完成吗?
是。队列指示执行策略,而不是如何对块进行排队。
换句话说,如果队列是串行的,则使用异步或同步进行排队不会改变该行为。唯一的区别是:在继续执行其余程序之前,我是否等待此块完成? dispatch_async
=不,dispatch_sync
=是。
如果我将异步提交到串行队列的任务#23怎么办? 需要很长时间才能完成?
没有任何改变。串行队列始终在出列并执行下一个块(#24)之前等待先前出列的块(#23)完成。如果需要停止队列,则应在块代码中实现超时。