【问题标题】:Understanding dispatch_queues and synchronous/asynchronous dispatch了解 dispatch_queues 和同步/异步调度
【发布时间】:2017-02-03 23:22:42
【问题描述】:

我是一名 Android 工程师,试图移植一些使用 5 个串行调度队列的 iOS 代码。我想确保我以正确的方式思考问题。

  • dispatch_sync 到 SERIAL 队列基本上是将队列用作同步队列 - 只有一个线程可以访问它,并且可以将执行的块视为关键区域。它立即发生在当前线程上——相当于

    get_semaphore()
    queue.pop()
    do_block()
    release_semaphore()
    
  • dispatch_async 到串行队列 - 在另一个线程上执行块并让当前线程立即返回。然而,由于它是一个串行队列,它保证一次只会执行其中一个异步线程(对 dispatch_async 的下一次调用将等到所有其他线程完成)。该块也可以被认为是一个关键区域,但它会发生在另一个线程上。所以与上面的代码相同,但它首先传递给工作线程。

我是不是搞错了,还是我猜对了?

【问题讨论】:

    标签: ios multithreading ios-multithreading


    【解决方案1】:

    这感觉像是一种过于复杂的思考方式,并且该描述中有很多不完全正确的小细节。具体来说,“它立即发生在当前线程上”是不正确的。

    首先,让我们退后一步:dispatch_asyncdispatch_sync 之间的区别仅仅是当前线程是否等待它。但是,当您将某些内容分派到串行队列时,您应该始终想象它运行在 GCD 自己选择的单独工作线程上。是的,作为一种优化,有时dispatch_sync 会使用当前线程,但你无法保证这一事实。

    其次,当您讨论dispatch_sync 时,您会说它“立即”运行。但这绝不能保证立即生效。如果一个线程对某个串行队列执行dispatch_sync,则该线程将阻塞,直到(a)当前在该串行队列上运行的任何块完成; (b) 该串行队列运行和完成的任何其他排队块; (c) 显然,线程 A 本身分派的块运行并完成。

    现在,当您使用串行队列进行同步时,一些线程安全地访问内存中的某个对象,通常该同步过程非常快,因此等待线程通常会被阻塞一段可忽略不计的时间,因为已调度的块(以及任何先前已调度的块)来完成。但总的来说,说它会立即运行是一种误导。 (如果它总是可以立即运行,那么您就不需要队列来同步访问)。


    现在您的问题涉及“关键区域”,我假设您正在谈论一些代码,为了确保线程安全或出于其他类似原因,必须同步这些代码。因此,当运行此代码以进行同步时,dispatch_syncdispatch_async 的唯一问题是当前线程是否必须等待。例如,一种常见的模式是说一个可能dispatch_async 写入某个模型(因为在继续之前无需等待模型更新),但dispatch_sync 从某个模型读取(因为你显然不'在返回读取值之前不想继续)。

    该同步/异步模式的进一步优化是读写器模式,其中允许并发读取但不允许并发写入。因此,您将使用并发队列,dispatch_barrier_async 写入(实现类似串行的写入行为),但 dispatch_sync 读取(相对于其他读取操作享受并发性能)。

    【讨论】:

    【解决方案2】:

    为了挑剔,dispatch_sync 不一定在当前线程上运行代码,但如果没有,它仍然会阻塞当前线程,直到任务完成。仅当您依赖线程 ID 或线程本地存储时,这种区别才可能很重要。

    但除此之外,是的,除非我错过了一些微妙的东西。

    【讨论】:

    • 很高兴知道。修改后的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-24
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 2016-09-28
    • 2014-01-09
    相关资源
    最近更新 更多