需要一些关于调度队列,线程和NSRunLoop的说明

时间:2021-06-11 00:05:16

The following things are what I know & understand:

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

Global queue is a concurrent queue which can dispatch tasks to multiple threads. The order of executing task is not guaranteed. e.g.:

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

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

If I want to dispatch to serial queue, I can use

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

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

each time only one task is dispatched to a thread & get executed. The order is FIFO.

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

===== What I am confused & not fully understand =======

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

  1. The main thread has a NSRunLoop, looping tasks in main thread. I am wondering what is the relation ship between dispatch queue and run loop? Can I understand it like, if dispatching a task to main thread, the main thread's NSRunLoop get the dispatched task and execute it?

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

  2. What about global queue which dispatching tasks to multiple threads? Does iOS/OSX system automatically create not only the threads, but also create NSRunLoop for each thread? and then the run loop in each thread get the dispatched task from global queue & execute it?

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

  3. Who knows the thread? Do dispatch_async() and dispatch_sync() function know to which thread to dispatch task or does the queue knows to which thread to dispatch task?

    谁知道线程? dispatch_async()和dispatch_sync()函数是否知道要调度任务的线程或队列是否知道要调度任务的线程?

  4. Is there a way to get NSRunLoop object of the thread(to which the task is dispatched) from dispatch queue programmatically? (this question is related with question 3)

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

2 个解决方案

#1


20  

  1. The main thread's run loop has a step in which it runs any blocks queued on the main queue. You might find this answer useful if you want to understand what the run loop does in detail.

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

  2. GCD creates the threads for concurrent queues. A thread doesn't have a run loop until the first time something running on the thread asks for the thread's run loop, at which point the system creates a run loop for the thread. However, the run loop only runs if something on that thread then asks it to run (by calling -[NSRunLoop run] or CFRunLoopRun or similar). Most threads, including threads created for GCD queues, never have a run loop.

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

  3. GCD manages a pool of threads and, when it needs to run a block (because it was added to some queue), GCD picks the thread on which to run the block. GCD's thread-choosing algorithm is mostly an implementation detail, except that it will always choose the main thread for a block that was added to the main queue. (Note that GCD will also sometimes use the main thread for a block added to some other queue.)

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

  4. You can only get the run loop of the main thread (using +[NSRunLoop mainRunLoop] or CFRunLoopGetMain) or the run loop of the current thread (using +[NSRunLoop currentRunLoop] or CFRunLoopGetCurrent). If you need the run loop of some arbitrary thread, you must find a way to call CFRunLoopGetCurrent on that thread and pass its return value back across threads in a safe, synchronized way.

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

    Please note that the NSRunLoop interface is not thread safe, but the CFRunLoop interface is thread safe, so if you need to access another thread's run loop, you should use the CFRunLoop interface.

    请注意,NSRunLoop接口不是线程安全的,但CFRunLoop接口是线程安全的,因此如果您需要访问另一个线程的运行循环,则应使用CFRunLoop接口。

    Also note that you should probably not run a run loop for very long inside a block running on a GCD queue, because you're tying up a thread that GCD expects to control. If you need to run a run loop for a long time, you should start your own thread for it. You can see an example of this in the _legacyStreamRunLoop function in CFStream.c. Note how it makes the dedicated thread's run loop available in a static variable named sLegacyRL, which it initializes under the protection of a dispatch_semaphore_t.

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

#2


3  

  1. The relationship between the main thread's run loop and the main dispatch queue is merely that they're both run on the main thread and that blocks dispatched to main queue are interleaved on the main thread with events processed on the main runloop.

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

    As the Concurrency Programming Guide says:

    正如并发编程指南所说:

    The main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread. This queue works with the application’s run loop (if one is present) to interleave the execution of queued tasks with the execution of other event sources attached to the run loop. Because it runs on your application’s main thread, the main queue is often used as a key synchronization point for an application.

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

  2. When dispatching to background thread, it does not create a NSRunLoop for those worker threads. Nor do you generally need a run loop for these background threads. We used to have to create our own NSRunLoop for background threads (e.g. when scheduling NSURLConnection on background thread), but this pattern is not required very often anymore.

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

    For things historically requiring run loops, there are often better mechanisms if running them on a background thread. For example, rather than NSURLConnection, you'd now use NSURLSession. Or, rather than NSTimer on NSRunLoop on background thread, you'd create a GCD timer dispatch source.

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

  3. Regarding who "knows" the thread, the worker thread is identified when dispatched to a queue. The thread is not a property of the queue, but rather assigned to the queue when the queue needs it.

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

  4. If you want to create a NSRunLoop for a worker thread (which you generally shouldn't be doing, anyway), you create it and keep track of it yourself. And, when scheduling a thread with a run loop, I would be inclined to create the NSThread myself and schedule the run loop on that, rather than tying up one of GCD's worker threads.

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

#1


20  

  1. The main thread's run loop has a step in which it runs any blocks queued on the main queue. You might find this answer useful if you want to understand what the run loop does in detail.

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

  2. GCD creates the threads for concurrent queues. A thread doesn't have a run loop until the first time something running on the thread asks for the thread's run loop, at which point the system creates a run loop for the thread. However, the run loop only runs if something on that thread then asks it to run (by calling -[NSRunLoop run] or CFRunLoopRun or similar). Most threads, including threads created for GCD queues, never have a run loop.

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

  3. GCD manages a pool of threads and, when it needs to run a block (because it was added to some queue), GCD picks the thread on which to run the block. GCD's thread-choosing algorithm is mostly an implementation detail, except that it will always choose the main thread for a block that was added to the main queue. (Note that GCD will also sometimes use the main thread for a block added to some other queue.)

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

  4. You can only get the run loop of the main thread (using +[NSRunLoop mainRunLoop] or CFRunLoopGetMain) or the run loop of the current thread (using +[NSRunLoop currentRunLoop] or CFRunLoopGetCurrent). If you need the run loop of some arbitrary thread, you must find a way to call CFRunLoopGetCurrent on that thread and pass its return value back across threads in a safe, synchronized way.

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

    Please note that the NSRunLoop interface is not thread safe, but the CFRunLoop interface is thread safe, so if you need to access another thread's run loop, you should use the CFRunLoop interface.

    请注意,NSRunLoop接口不是线程安全的,但CFRunLoop接口是线程安全的,因此如果您需要访问另一个线程的运行循环,则应使用CFRunLoop接口。

    Also note that you should probably not run a run loop for very long inside a block running on a GCD queue, because you're tying up a thread that GCD expects to control. If you need to run a run loop for a long time, you should start your own thread for it. You can see an example of this in the _legacyStreamRunLoop function in CFStream.c. Note how it makes the dedicated thread's run loop available in a static variable named sLegacyRL, which it initializes under the protection of a dispatch_semaphore_t.

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

#2


3  

  1. The relationship between the main thread's run loop and the main dispatch queue is merely that they're both run on the main thread and that blocks dispatched to main queue are interleaved on the main thread with events processed on the main runloop.

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

    As the Concurrency Programming Guide says:

    正如并发编程指南所说:

    The main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread. This queue works with the application’s run loop (if one is present) to interleave the execution of queued tasks with the execution of other event sources attached to the run loop. Because it runs on your application’s main thread, the main queue is often used as a key synchronization point for an application.

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

  2. When dispatching to background thread, it does not create a NSRunLoop for those worker threads. Nor do you generally need a run loop for these background threads. We used to have to create our own NSRunLoop for background threads (e.g. when scheduling NSURLConnection on background thread), but this pattern is not required very often anymore.

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

    For things historically requiring run loops, there are often better mechanisms if running them on a background thread. For example, rather than NSURLConnection, you'd now use NSURLSession. Or, rather than NSTimer on NSRunLoop on background thread, you'd create a GCD timer dispatch source.

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

  3. Regarding who "knows" the thread, the worker thread is identified when dispatched to a queue. The thread is not a property of the queue, but rather assigned to the queue when the queue needs it.

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

  4. If you want to create a NSRunLoop for a worker thread (which you generally shouldn't be doing, anyway), you create it and keep track of it yourself. And, when scheduling a thread with a run loop, I would be inclined to create the NSThread myself and schedule the run loop on that, rather than tying up one of GCD's worker threads.

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