答案 0 :(得分:2)
Grand Central Dispatch(GCD)提供了一个“主队列”(可以使用DispatchQueue.main
在Swift中访问)。主队列总是在主线程上运行它的块。
由于Apple平台上的应用程序通常在主线程上运行RunLoop.main
,因此运行循环与GCD一起运行,以运行添加到主队列的块。
因此,当创建主线程的运行循环时,它会创建一些GCD对象,这使得GCD自身初始化。 GCD初始化的一部分涉及创建“工作队列”和用于运行添加到工作队列的作业的线程池。
您可以看到创建线程的是运行循环的创建,而不是它的运行。这是一个示例程序:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
[NSRunLoop currentRunLoop]; // line 4
return 0; // line 5
}
在终端中,我运行lldb(调试器)。我告诉它调试test
程序,在第4行设置断点,然后运行。当它在断点处停止时(在调用currentRunLoop
之前,我列出所有线程:
:; lldb
"crashlog" and "save_crashlog" command installed, use the "--help" option for detailed help
(lldb) target create test
Current executable set to 'test' (x86_64).
(lldb) b 4
Breakpoint 1: where = test`main + 22 at main.m:4, address = 0x0000000100000f46
(lldb) r
Process 12087 launched: '/Users/mayoff/Library/Developer/Xcode/DerivedData/test-aegotyskrtnbeabaungzpkkbjvdz/Build/Products/Debug/test' (x86_64)
Process 12087 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: sp=0x00007fff5fbff240 fp=0x00007fff5fbff260 pc=0x0000000100000f46 test`main(argc=1, argv=0x00007fff5fbff280) + 22 at main.m:4
1 #import <Foundation/Foundation.h>
2
3 int main(int argc, const char * argv[]) {
-> 4 [NSRunLoop currentRunLoop]; // line 4
5 return 0; // line 5
6 }
Target 0: (test) stopped.
(lldb) thread list
Process 12087 stopped
* thread #1: tid = 0x1066d3, 0x0000000100000f46 test`main(argc=1, argv=0x00007fff5fbff280) at main.m:4, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
只有一个主题。接下来,我逐步调用currentRunLoop
并再次列出所有线程:
(lldb) n
Process 12087 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
frame #0: sp=0x00007fff5fbff240 fp=0x00007fff5fbff260 pc=0x0000000100000f69 test`main(argc=1, argv=0x00007fff5fbff280) + 57 at main.m:5
2
3 int main(int argc, const char * argv[]) {
4 [NSRunLoop currentRunLoop]; // line 4
-> 5 return 0; // line 5
6 }
Target 0: (test) stopped.
(lldb) thread list
Process 12087 stopped
* thread #1: tid = 0x1066d3, 0x0000000100000f69 test`main(argc=1, argv=0x00007fff5fbff280) at main.m:5, queue = 'com.apple.main-thread', stop reason = step over
thread #2: tid = 0x106ab3, 0x00007fffc942c070 libsystem_pthread.dylib`start_wqthread
thread #3: tid = 0x106ab4, 0x00007fffc934244e libsystem_kernel.dylib`__workq_kernreturn + 10
thread #4: tid = 0x106ab5, 0x00007fffc8923e85 libobjc.A.dylib`class_createInstance + 142, queue = 'com.apple.root.default-qos.overcommit'
现在有四个线程,其中一些在初始化过程中停止。
“但Rob ,”你说,“当我在Xcode中运行test
并停止之前调用currentRunLoop
时,它已经有四个线程!“如图所示:
“确实如此,”我回答。如果运行菜单项Debug&gt;调试工作流程&gt;共享库...,并在过滤器框中键入Xcode,您可以找到原因:
当您在Xcode下运行程序时,Xcode会在您的进程中注入一些额外的共享库,以提供额外的调试支持。这些共享库包括在代码运行之前运行的初始化代码,以及初始化代码使用GCD执行某些操作,因此在第一行代码运行之前GCD会被初始化(创建其线程池)。
工作队列根据工作负载调整其线程池的大小。由于没有任何操作将队列添加到队列中,因此它会立即将其池缩减为仅一个后台线程。这就是为什么当你查看Xcode的CPU报告时,你只能看到两个线程:运行运行循环的主线程,以及一个等待作业运行的工作线程。