需要一些关于调度队列、线程和 NSRunLoop 的说明
need some clarifications about dispatch queue, thread and NSRunLoop
以下是我所知道和理解的:
全局队列是一个并发队列,可以将任务分派给多个线程。不保证执行任务的顺序。例如:
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) {
...
}
每次只有一个任务被分派到一个线程并被执行。顺序是先进先出。
=====我很困惑&不完全理解=======
主线程有一个NSRunLoop,在主线程中循环任务。我想知道调度队列和 运行 循环之间的关系是什么?
可以这么理解吗,分派任务给主线程,主线程的NSRunLoop拿到分派的任务执行?
全局队列将任务分派到多个线程呢? iOS/OSX 系统是否不仅自动创建线程,还为每个线程创建NSRunLoop?然后每个线程中的 运行 循环从全局队列中获取分派的任务并执行它?
谁知道线程? dispatch_async()
和 dispatch_sync()
函数是否知道将任务分派给哪个线程,或者 queue 是否知道将任务分派给哪个线程?
有没有办法以编程方式从调度队列中获取线程(任务被调度到的线程)的 NSRunLoop 对象? (这个问题与问题3有关)
主线程的运行循环和主调度队列之间的关系仅仅是它们都在主线程上运行并且调度到主队列的块在主线程上与在主 运行 循环中处理的事件交错。
正如 Concurrency Programming Guide 所说:
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 synchronization point for an application.
当分派到后台线程时,它不会为那些工作线程创建NSRunLoop
。这些后台线程通常也不需要 运行 循环。我们过去必须为后台线程创建自己的 NSRunLoop
(例如,在后台线程上调度 NSURLConnection
时),但现在不再需要这种模式了。
对于历史上需要 运行 循环的事情,如果 运行 在后台线程上运行它们通常会有更好的机制。例如,您现在可以使用 NSURLSession
,而不是 NSURLConnection
。或者,您可以创建一个 GCD 计时器调度源,而不是在后台线程 NSRunLoop
上 NSTimer
。
关于谁“知道”线程,工作线程在被分派到队列时被识别。该线程不是队列的属性,而是队列需要时从线程池中选择一个
如果您想为工作线程创建一个 NSRunLoop
(无论如何您通常不应该这样做),您可以自己创建并跟踪它。如果您调用 current
,它将为您创建一个 运行 循环:“如果该线程尚不存在 运行 循环,则会创建并返回一个。”
并且,在使用 运行 循环调度线程时,我倾向于自己创建 NSThread
并在其上调度 运行 循环,而不是占用一个GCD 的工作线程数量非常有限。
主线程的 运行 循环有一个步骤,在该步骤中它 运行s 任何在主队列中排队的块。如果您想详细了解 运行 循环的作用,您可能会发现 this answer 很有用。
GCD 为并发队列创建线程。线程没有 运行 循环,直到线程上的某些 运行ning 第一次请求线程的 运行 循环,此时系统创建 运行循环线程。但是,运行 循环仅 运行s 如果该线程上的某些内容然后要求它 运行 (通过调用 -[NSRunLoop run]
或 CFRunLoopRun
或类似的)。大多数线程,包括为 GCD 队列创建的线程,从来没有 运行 循环。
GCD 管理一个线程池,当它需要 运行 一个块时(因为它被添加到某个队列中),GCD 会选择线程 运行 块。 GCD 的线程选择算法主要是一个实现细节,除了它将总是 为添加到主队列的块选择主线程。 (请注意,GCD 有时也会将主线程用于添加到其他队列的块。)
只能获取主线程的运行循环(使用+[NSRunLoop mainRunLoop]
或CFRunLoopGetMain
)或者当前线程的运行循环线程(使用 +[NSRunLoop currentRunLoop]
或 CFRunLoopGetCurrent
)。如果你需要某个任意线程的 运行 循环,你必须找到一种方法在该线程上调用 CFRunLoopGetCurrent
并以安全、同步的方式跨线程传递它的 return 值。
请注意NSRunLoop
接口不是线程安全的,但是CFRunLoop
接口是线程安全,所以如果你需要访问另一个线程的 运行 循环,你应该使用 CFRunLoop
接口。
另请注意,您可能不应该 运行 一个 运行 循环在 GCD 队列上 运行ning 块内很长时间,因为您正在占用一个线程GCD 期望控制。如果你需要长时间运行一个运行循环,你应该为它启动自己的线程。您可以在 the _legacyStreamRunLoop
function in CFStream.c 中看到这方面的示例。请注意它如何使专用线程的 运行 循环在名为 sLegacyRL
的静态变量中可用,它在 dispatch_semaphore_t
.
的保护下进行初始化
以下是我所知道和理解的:
全局队列是一个并发队列,可以将任务分派给多个线程。不保证执行任务的顺序。例如:
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) {
...
}
每次只有一个任务被分派到一个线程并被执行。顺序是先进先出。
=====我很困惑&不完全理解=======
主线程有一个NSRunLoop,在主线程中循环任务。我想知道调度队列和 运行 循环之间的关系是什么? 可以这么理解吗,分派任务给主线程,主线程的NSRunLoop拿到分派的任务执行?
全局队列将任务分派到多个线程呢? iOS/OSX 系统是否不仅自动创建线程,还为每个线程创建NSRunLoop?然后每个线程中的 运行 循环从全局队列中获取分派的任务并执行它?
谁知道线程?
dispatch_async()
和dispatch_sync()
函数是否知道将任务分派给哪个线程,或者 queue 是否知道将任务分派给哪个线程?有没有办法以编程方式从调度队列中获取线程(任务被调度到的线程)的 NSRunLoop 对象? (这个问题与问题3有关)
主线程的运行循环和主调度队列之间的关系仅仅是它们都在主线程上运行并且调度到主队列的块在主线程上与在主 运行 循环中处理的事件交错。
正如 Concurrency Programming Guide 所说:
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 synchronization point for an application.
当分派到后台线程时,它不会为那些工作线程创建
NSRunLoop
。这些后台线程通常也不需要 运行 循环。我们过去必须为后台线程创建自己的NSRunLoop
(例如,在后台线程上调度NSURLConnection
时),但现在不再需要这种模式了。对于历史上需要 运行 循环的事情,如果 运行 在后台线程上运行它们通常会有更好的机制。例如,您现在可以使用
NSURLSession
,而不是NSURLConnection
。或者,您可以创建一个 GCD 计时器调度源,而不是在后台线程NSRunLoop
上NSTimer
。关于谁“知道”线程,工作线程在被分派到队列时被识别。该线程不是队列的属性,而是队列需要时从线程池中选择一个
如果您想为工作线程创建一个
NSRunLoop
(无论如何您通常不应该这样做),您可以自己创建并跟踪它。如果您调用current
,它将为您创建一个 运行 循环:“如果该线程尚不存在 运行 循环,则会创建并返回一个。”并且,在使用 运行 循环调度线程时,我倾向于自己创建
NSThread
并在其上调度 运行 循环,而不是占用一个GCD 的工作线程数量非常有限。
主线程的 运行 循环有一个步骤,在该步骤中它 运行s 任何在主队列中排队的块。如果您想详细了解 运行 循环的作用,您可能会发现 this answer 很有用。
GCD 为并发队列创建线程。线程没有 运行 循环,直到线程上的某些 运行ning 第一次请求线程的 运行 循环,此时系统创建 运行循环线程。但是,运行 循环仅 运行s 如果该线程上的某些内容然后要求它 运行 (通过调用
-[NSRunLoop run]
或CFRunLoopRun
或类似的)。大多数线程,包括为 GCD 队列创建的线程,从来没有 运行 循环。GCD 管理一个线程池,当它需要 运行 一个块时(因为它被添加到某个队列中),GCD 会选择线程 运行 块。 GCD 的线程选择算法主要是一个实现细节,除了它将总是 为添加到主队列的块选择主线程。 (请注意,GCD 有时也会将主线程用于添加到其他队列的块。)
只能获取主线程的运行循环(使用
+[NSRunLoop mainRunLoop]
或CFRunLoopGetMain
)或者当前线程的运行循环线程(使用+[NSRunLoop currentRunLoop]
或CFRunLoopGetCurrent
)。如果你需要某个任意线程的 运行 循环,你必须找到一种方法在该线程上调用CFRunLoopGetCurrent
并以安全、同步的方式跨线程传递它的 return 值。请注意
NSRunLoop
接口不是线程安全的,但是CFRunLoop
接口是线程安全,所以如果你需要访问另一个线程的 运行 循环,你应该使用CFRunLoop
接口。另请注意,您可能不应该 运行 一个 运行 循环在 GCD 队列上 运行ning 块内很长时间,因为您正在占用一个线程GCD 期望控制。如果你需要长时间运行一个运行循环,你应该为它启动自己的线程。您可以在 the
_legacyStreamRunLoop
function in CFStream.c 中看到这方面的示例。请注意它如何使专用线程的 运行 循环在名为sLegacyRL
的静态变量中可用,它在dispatch_semaphore_t
. 的保护下进行初始化