1

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

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

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

我是不是在其中任何一个,或者我是否正确地弄清楚了?

4

2 回答 2

1

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

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

于 2017-02-03T23:44:41.530 回答
1

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

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

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

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


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

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

于 2017-02-04T01:18:38.170 回答