串行调度队列如何保证资源保护?

use*_*719 2 concurrency objective-c grand-central-dispatch ios dispatch-async

//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];

});
Run Code Online (Sandbox Code Playgroud)

如果我提交100个访问并改变银行账户余额的任务,我理解串行队列将按顺序执行每个任务,但是在使用dispatch_async时这些任务是否也按顺序完成?

如果我异步提交到串行队列的任务#23需要很长时间才能完成怎么办?任务#24只在任务#23完成时启动,还是任务#24在任务#23完成之前启动?如果是这样,任务#24在启动其工作时是否有错误的银行账户余额,从而搞砸数据完整性?

谢谢!!

Rob*_*Rob 7

是的,专用串行队列是同步访问多个线程之间共享的某些资源的绝佳方式.并且,是的,使用串行队列,每个任务将等待前一个任务完成.

两点意见:

  1. 虽然这听起来像是一个非常低效的过程,但这隐含在任何同步技术(无论是基于队列还是基于锁的方法)的核心,其目标是最小化共享资源的并发更新.

    但在许多情况下,串行队列技术可以比其他常用技术(例如简单的互斥锁NSLock@synchronized指令)产生明显更好的性能.有关备用同步技术的讨论,请参阅" 线程编程指南"的" 同步"部分.有关使用队列代替锁的讨论,请参阅" 并发编程指南"中的"从线程迁移中删除基于锁的代码"部分.

  2. 串行队列模式的一种变体是使用"读写器"模式,您可以在其中创建GCD并发队列:

    queue = dispatch_queue_create("identifier", DISPATCH_QUEUE_CONCURRENT);
    
    Run Code Online (Sandbox Code Playgroud)

    然后使用执行读取dispatch_sync,但使用执行写入dispatch_barrier_async.净有效是允许并发读操作,但确保永远不会同时执行写操作.

    如果您的资源允许并发读取,那么读写器模式可以提供比串行队列更高的性能.

因此,简而言之,虽然让任务#24等待任务#23似乎效率低下,但这是任何同步技术中固有的,在这种技术中,您努力最小化共享资源的并发更新.GCD串行队列是一种令人惊讶的高效机制,通常比许多简单的锁定机制更好.在某些情况下,读写器模式可以提供进一步的性能改进.


我在下面的原始答案是对原始问题的回应,该问题令人困惑,标题为"串行调度队列如何保证并发?" 回想起来,这只是偶然使用了错误的术语.


这是一个有趣的词语选择,"串行调度队列如何保证并发?"

三种类型的队列,串行,并发和主队列.顾名思义,串行队列将不会启动下一个调度块,直到前一个调度块完成.(使用你的例子,这意味着如果任务23花了很长时间,它就不会启动任务24直到它完成.)有时候这是至关重要的(例如,如果任务24取决于任务23的结果或任务23)和24正试图访问相同的共享资源).

如果您希望这些不同的调度任务相互并发运行,则可以使用并发队列(您可以使用其中一个全局并发队列,dispatch_get_global_queue也可以使用dispatch_queue_createDISPATCH_QUEUE_CONCURRENT选项创建自己的并发队列).在并发队列中,许多已分派的任务可以并发运行.使用并发队列需要一些小心(特别是共享资源的同步),但在正确实施时可以产生显着的性能优势.

作为这两种方法之间的折衷方案,您可以使用操作队列,这些操作队列既可以是并发的,也可以通过设置限制队列中的任意操作在任何给定时间同时运行maxConcurrentOperationCount.您将使用此功能的典型方案是在执行后台网络任务时,您不需要超过五个并发网络请求.

有关更多信息,请参阅" 并发编程指南".


Jan*_*ano 5

man dispatch_queue_create说:“由分派到串行队列的块执行的所有内存写入都保证对分派到同一队列的后续块可见。” 因此,串行队列是序列化对可变状态的访问以避免竞争条件的好方法。

使用 dispatch_async 时,这些任务是否也按顺序完成?

是的。队列决定了执行策略,而不是你如何对块进行排队。

换句话说,如果队列是串行的,使用 async 或 sync 排队不会改变这一点。唯一的区别是:在继续执行程序的其余部分之前,我是否要等待该块完成?dispatch_async=没有,dispatch_sync=是。

如果我异步提交到串行队列的任务 #23 需要很长时间才能完成怎么办?

没有什么改变。串行队列总是等待先前出列的块(#23)完成,然后再出列并执行下一个块(#24)。如果延迟队列是一个问题,您应该在块代码中实现超时。