我使用RunLoop進行實驗。我創建了簡單的Mac OS控制檯應用程序,並只調用一行代碼。爲什麼在Mac OS控制檯應用程序中對runloop.run的調用方法會創建其他線程?
RunLoop.current.run()
之後在調試導航器出現第二個線程。爲什麼?
我使用RunLoop進行實驗。我創建了簡單的Mac OS控制檯應用程序,並只調用一行代碼。爲什麼在Mac OS控制檯應用程序中對runloop.run的調用方法會創建其他線程?
RunLoop.current.run()
之後在調試導航器出現第二個線程。爲什麼?
大中央調度(GCD)提供了一個「主隊列」(使用DispatchQueue.main
斯威夫特訪問)。主隊列總是在主線程上運行其塊。
由於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'
現在有四個線程,並且其中一些停在初始化的中間。
「但羅布,」你說,「當我在Xcode運行test
和停止調用currentRunLoop
之前,它已經擁有四個線程「如下所示:
!‘的確如此,’我回答。如果您運行的菜單項調試>調試工作流程>共享庫......,和類型的Xcode進入過濾器中,你可以發現爲什麼:
當您運行的Xcode下的程序,Xcode中注入一些額外的共享庫進入您的流程以提供額外的調試支持。這些共享庫包括在代碼運行之前運行的初始化代碼,以及初始化代碼對GCD執行某些操作,因此GCD在第一行代碼運行之前被初始化(創建其線程池)。
工作隊列根據工作負載調整其線程池的大小。由於沒有任何內容將作業添加到隊列中,因此它立即將其池縮小爲只有一個後臺線程。這就是爲什麼當您查看Xcode的CPU報告時,您只會看到兩個線程:運行循環的主線程和等待作業運行的一個工作線程。
謝謝您的詳細解釋!你怎麼知道的?我找不到在蘋果的信息( –
我想通了通過調試在終端和一些猜測的考驗。[這個頁面](http://newosxbook.com/articles/GCD.html)有許多有趣的信息,但直到發佈我的答案之後我才發現它。 –