需要对调度队列,线程和NSRunLoop做一些澄清

Lee*_*fin 21 multithreading nsthread grand-central-dispatch nsrunloop ios

以下是我所了解和理解的事情:

全局队列是一个并发队列,可以将任务分派给多个线程.执行任务的顺序无法保证.例如:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
 for (int i; i<10; i++) {
  doTask()
 }
})
Run Code Online (Sandbox Code Playgroud)

如果我想调度到串口队列,我可以使用

dispatch_async(dispatch_queue_create("my.serial.queue", nil) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

每次只有一个任务被分派到一个线程并被执行.订单是FIFO.

=====我感到困惑和不完全理解=======

  1. 主线程有一个NSRunLoop,在主线程中循环任务.我想知道调度队列和运行循环之间的关系是什么?我可以理解它,如果将任务调度到主线程,主线程的NSRunLoop获取调度任务并执行它吗?

  2. 将任务分派给多个线程的全局队列怎么样?iOS/OSX系统是否自动创建线程,还为每个线程创建NSRunLoop?然后每个线程中的运行循环从全局队列中获取调度任务并执行它?

  3. 谁知道线程?do dispatch_async()dispatch_sync()function知道哪个线程要调度任务,或者队列知道哪个线程要调度任务?

  4. 有没有办法以编程方式从调度队列中获取线程的NSRunLoop对象(调度任务的对象)?(这个问题与问题3有关)

rob*_*off 27

  1. 主线程的运行循环有一个步骤,它运行在主队列上排队的任何块.如果您想了解运行循环的详细信息,您可能会发现此答案很有用.

  2. GCD为并发队列创建线程.线程没有运行循环,直到第一次在线程上运行的东西要求线程的运行循环,此时系统为该线程创建一个运行循环.然而,运行循环只运行,如果在该线程的东西,然后问它来运行(通过调用-[NSRunLoop run]CFRunLoopRun或类似).大多数线程(包括为GCD队列创建的线程)从不具有运行循环.

  3. GCD管理一个线程池,当它需要运行一个块时(因为它被添加到某个队列中),GCD选择运行该块的线程.GCD的线程选择算法主要是一个实现细节,除了它总是选择添加到主队列的块的主线程.(注意,GCD有时也会将主线程用于添加到其他队列的块.)

  4. 您只能获取主线程的运行循环(使用+[NSRunLoop mainRunLoop]CFRunLoopGetMain)或当前线程的运行循环(使用+[NSRunLoop currentRunLoop]CFRunLoopGetCurrent).如果您需要某个任意线程的运行循环,您必须找到一种方法来调用CFRunLoopGetCurrent该线程并以安全,同步的方式将其返回值传回线程.

    请注意,NSRunLoop接口不是线程安全的,但CFRunLoop接口线程安全的,所以如果你需要访问另一个线程的运行循环,你应该使用该CFRunLoop接口.

    另请注意,您可能不应在GCD队列上运行的块内运行很长时间的运行循环,因为您正在占用GCD期望控制的线程.如果你需要长时间运行一个运行循环,你应该为它启动自己的线程.您可以在CFStream.c_legacyStreamRunLoop函数中看到此示例.请注意它如何使专用线程的运行循环在名为的静态变量中可用sLegacyRL,它在a的保护下初始化dispatch_semaphore_t.


Rob*_*Rob 8

  1. 主线程的 run loop 和主调度队列之间的关系仅仅是它们都在主线程上运行,并且调度到主队列的块在主线程上与在主 runloop 上处理的事件交错。

    正如并发编程指南所说:

    主调度队列是一个全局可用的串行队列,它在应用程序的主线程上执行任务。该队列与应用程序的运行循环(如果存在)一起工作,以将排队任务的执行与附加到运行循环的其他事件源的执行交错​​。因为它在应用程序的主线程上运行,所以主队列通常用作应用程序的关键同步点。

  2. 当调度到后台线程,它并没有创造NSRunLoop这些工作线程。您通常也不需要这些后台线程的运行循环。我们过去必须NSRunLoop为后台线程创建我们自己的(例如NSURLConnection在后台线程上调度时),但现在不再经常需要这种模式。

    对于历史上需要运行循环的事情,如果在后台线程上运行它们,通常会有更好的机制。例如,NSURLConnection您现在将使用,而不是NSURLSession。或者,您可以创建一个 GCD 计时器调度源,而不是NSTimerNSRunLoop后台线程上。

  3. 关于谁“知道”线程,工作线程在被分派到队列时被识别。线程不是队列的属性,而是在队列需要时分配给队列。

  4. 如果您想NSRunLoop为工作线程创建一个(无论如何您通常不应该这样做),您可以创建它并自己跟踪它。而且,当使用运行循环调度线程时,我倾向于创建NSThread自己并在其上调度运行循环,而不是占用 GCD 的工作线程之一。