需要对调度队列,线程和NSRunLoop

时间:2016-06-23 19:57:53

标签: ios multithreading grand-central-dispatch nsthread nsrunloop

以下是我所知道的事情。理解:

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

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
 for (int i; i<10; i++) {
  doTask()
 }
})

如果我想发送到串行队列,我可以使用

dispatch_async(dispatch_queue_create("my.serial.queue", nil) {
  ...
}

每次只有一个任务被分派到一个线程&amp;得到执行。订单是FIFO。

=====我很困惑&amp;不完全明白=======

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

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

  3. 谁知道线程? dispatch_async()dispatch_sync()函数知道要分派任务的线程,或者队列知道要分派任务的线程吗?

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

2 个答案:

答案 0 :(得分:27)

  1. 主线程的运行循环有一个步骤,它运行在主队列中排队的任何块。如果您想了解运行循环的详细信息,您可能会发现this answer非常有用。

  2. GCD为并发队列创建线程。线程没有运行循环,直到第一次在线程上运行的东西要求线程的运行循环,此时系统为该线程创建一个运行循环。但是,只有当该线程上的某些内容要求它运行时(通过调用-[NSRunLoop run]CFRunLoopRun或类似内容),运行循环才会运行。大多数线程(包括为GCD队列创建的线程)都没有运行循环。

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

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

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

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

答案 1 :(得分:4)

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

    正如Concurrency Programming Guide所说:

      

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

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

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

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

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