多線程開發用了很久,但是一直沒去深入了解。長久以來一直有一些迷惑。直到深入了解后,才發現了以前的理解有不少錯誤的地方。
單線程等於同步,多線程等於異步
- 這種理解很直觀,畢竟只有一個線程怎么異步?
Node.js表示不服,我就是單線程,我也能異步。談一談Node中的異步和單線程。
看完這篇文章我明白了單線程也能異步,把IO等耗時的操作比作燒水,我可以在這個時候切菜,這就是異步啊。
等等,似乎有點不對,那io又誰來開啟,又誰來通知cpu我已經結束了呢?
Node.js異步IO的實現,這篇文章解決了我的疑惑。
- Node.js里面只有自己寫的代碼是跑在主線程上,但是內部並不是單線程的,由C編寫的底層開啟了線程做IO操作。
恍然大悟,我現在的理解就是,會有一個可運行的線程池在等待cpu的使用權。類似IO,網絡請求這種耗時干等的操作,線程會放到需要等待的線程池中(阻塞),不會獲取cpu的使用權,直到操作完成
這個理解了,並發和並行就很容易了。
- 每個線程獲得cpu的使用權的時間就是一個時間片,用完了就必須要等下次了。時間片非常短,人根本意識不到,感覺就是並行的,但其實只是"偽並行",也就是並發。
概念都講結束了,現在可以談談iOS的多線程了。其實理論都一樣,無非線程的獲得,開啟,結束等。但是iOS有個不同,他有GCD,那真是神器。
關於GCD的串行隊列,並行隊列,一直以來都有一個錯誤的理解:
隊列就是線程,async就是另開線程,sync就是阻塞線程
實踐才能出真知,要想真明白,async,sync,串行隊列,並行隊列,主隊列,還是要親自測一下才行。
- 主線程下,是否開啟新線程
//主隊列
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
});
//串行隊列
dispatch_queue_t ser_queue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
dispatch_async(ser_queue, ^{
NSLog(@"1-%@",[NSThread currentThread]);
});
dispatch_async(ser_queue, ^{
NSLog(@"2-%@",[NSThread currentThread]);
});
dispatch_sync(ser_queue, ^{
NSLog(@"3-%@",[NSThread currentThread]);
});
//並行隊列
dispatch_queue_t con_queue = dispatch_queue_create("並行", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(con_queue, ^{
NSLog(@"1-%@",[NSThread currentThread]);
});
dispatch_async(con_queue, ^{
NSLog(@"2-%@",[NSThread currentThread]);
});
dispatch_sync(con_queue, ^{
NSLog(@"3-%@",[NSThread currentThread]);
});
- 非主線程下,異步是否新開線程
dispatch_queue_t ser_queue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t con_queue = dispatch_queue_create("並行", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(ser_queue, ^{
NSLog(@"1-%@",[NSThread currentThread]);
dispatch_async(ser_queue, ^{
NSLog(@"1-%@",[NSThread currentThread]);
});
});
dispatch_async(ser_queue, ^{
NSLog(@"2-%@",[NSThread currentThread]);
dispatch_async(con_queue, ^{
NSLog(@"2-%@",[NSThread currentThread]);
});
});
dispatch_async(con_queue, ^{
NSLog(@"3-%@",[NSThread currentThread]);
dispatch_async(con_queue, ^{
NSLog(@"3-%@",[NSThread currentThread]);
});
});
dispatch_async(con_queue, ^{
NSLog(@"4-%@",[NSThread currentThread]);
dispatch_async(ser_queue, ^{
NSLog(@"4-%@",[NSThread currentThread]);
});
});
dispatch_async(ser_queue, ^{
NSLog(@"5-%@",[NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"5-%@",[NSThread currentThread]);
});
});
dispatch_async(con_queue, ^{
NSLog(@"6-%@",[NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"6-%@",[NSThread currentThread]);
});
});
- 非主線程下,同步是否新開線程
dispatch_async(ser_queue, ^{
NSLog(@"1-%@",[NSThread currentThread]);
dispatch_sync(ser_queue, ^{
NSLog(@"1-%@",[NSThread currentThread]);
});
});
dispatch_async(ser_queue, ^{
NSLog(@"2-%@",[NSThread currentThread]);
dispatch_sync(con_queue, ^{
NSLog(@"2-%@",[NSThread currentThread]);
});
});
dispatch_async(con_queue, ^{
NSLog(@"3-%@",[NSThread currentThread]);
dispatch_sync(con_queue, ^{
NSLog(@"3-%@",[NSThread currentThread]);
});
});
dispatch_async(con_queue, ^{
NSLog(@"4-%@",[NSThread currentThread]);
dispatch_sync(ser_queue, ^{
NSLog(@"4-%@",[NSThread currentThread]);
});
});
dispatch_async(ser_queue, ^{
NSLog(@"5-%@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"5-%@",[NSThread currentThread]);
});
});
dispatch_async(con_queue, ^{
NSLog(@"6-%@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"6-%@",[NSThread currentThread]);
});
});
結論:
結果就不貼出來了,還是自己親自測下比較好。看看自己的想法和答案是否一致可是一件很快樂的事情。
基本上覆蓋了所有可能。感覺更像是面向隊列來的,線程的調度是系統自己分配的。
測下來感覺就是回答了兩個問題:
- async什么時候會新開線程
- sync什么時候會導致死鎖
我的答案:
- 主隊列:一定會在主線程
- 串行隊列:async會開一條線程
- 並行隊列:async會開多條線程
- 死鎖:必須是串行隊列,其次提交block所在隊列和把block放進去的隊列是同一個。
- async不同的隊列,基本上是在不同的線程上。(存疑)
- 提交block的隊列和block放進去的隊列是同一個,不管串行並行,都在同一個線程上。(存疑)
GCD是神器,還有好多需要學習的地方,推薦幾篇經典的文章:
GCD掃盲篇,巧談GCD
GCD進階篇
死鎖,圖文並茂,清晰易懂