了解dispatch_queues和同步/异步调度

时间:2017-02-03 23:22:42

标签: ios multithreading ios-multithreading

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

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

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

在任何一种情况下我是否已经离开,或者我是否正确地解决了这个问题?

2 个答案:

答案 0 :(得分:1)

要选择nits,dispatch_sync不必在当前线程上运行代码,但如果没有,则它仍会阻塞当前线程,直到任务完成。如果您依赖线程ID或线程本地存储,那么区别只是潜在的重要。

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

答案 1 :(得分:1)

这感觉就像是一种过于复杂的思考方式,并且有很多关于描述的细节并不是很正确。具体而言,"它立即发生在当前线程上#34;是不正确的。

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

其次,当你讨论dispatch_sync时,你会立刻说出它正在运行"立即"。但它并不能确保立竿见影。如果某个线程对某个串行队列执行dispatch_sync,那么该线程将阻塞,直到(a)该串行队列上当前正在运行的任何块完成; (b)该串行队列的任何其他排队块运行并完成; (c)显然,线程A本身分派的块运行并完成。

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

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

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