GCD API 理解 (一)
資料先行
GCD 深入理解:第一部分
GCD 深入理解:第二部分
以上兩篇文章是關(guān)于GCD講的比較好的文章,翻譯自raywenderlich,該網(wǎng)站有很多關(guān)于iOS 開發(fā)的優(yōu)秀文章。
引子
iOS 開發(fā)中有三大進階性的技術(shù)點,分別是GCD、runtime 和runloop。其中GCD用的最多,runtime也有不少使用場景,runloop在系統(tǒng)的API里體現(xiàn)的比較多,項目里實際使用比較少。
一直都想就這三個技術(shù)點做一些總結(jié),沒事的時候可以回來復(fù)習(xí)鞏固一下,可是記錄了很多要寫的點,但是文章卻是一拖再拖。本文就記錄GCD的一些API自己的理解和用法等,遇到新的API也會補充進來。
擴展
pthread 也是C 語言API(pthread現(xiàn)在已經(jīng)基本看不到有使用的了),而NSThread 是Objective-C對pthread的封裝;雖然GCD也是C語言API,但是非常容易使用,而NSOpretion是Objective-C對GCD的進一步封裝。這是iOS 中幾種多線程技術(shù)的關(guān)系。
介紹
GCD(Grand Central Dispatch)是OS X與iOS 開發(fā)中一種多核開發(fā)的解決方案。GCD是在libdispatch框架內(nèi),而該框架是iOS 應(yīng)用默認的已經(jīng)包含進去了。
蘋果是在 OS X 10.6 和 iOS 4 中引入了 GCD,它是低層級的C語言 API。使用GCD,它能夠讓開發(fā)者更加方便、更加容易得使用多核CPU。而且我們開發(fā)者不需要再直接跟線程打交道了,只需要向隊列中添加代碼塊即可,而GCD 在后端其實是管理著一個線程池。GCD 不僅決定著我們的代碼塊將在哪個線程被執(zhí)行,它還根據(jù)可用的系統(tǒng)資源對這些線程進行管理。通過集中的管理線程,來緩解大量線程被創(chuàng)建的問題。
媽媽再也不用擔(dān)心我們自己來處理線程的創(chuàng)建和銷毀了
基本知識
1.串行隊列和并發(fā)隊列
串行隊列就是每次只有一個任務(wù)執(zhí)行,當(dāng)一個任務(wù)執(zhí)行完畢后,才會執(zhí)行下一個任務(wù)。
而并發(fā)隊列就是同時有多個任務(wù)在執(zhí)行,同時執(zhí)行的任務(wù)哪個先執(zhí)行完,我們根本不知道。
iOS 中的串行隊列有兩種:主隊列(dispatch_get_main_queue())、通過dispatch_queue_create創(chuàng)建的串行隊列
1.1創(chuàng)建串行隊列
主隊列是系統(tǒng)為我們的應(yīng)用創(chuàng)建的,我們是沒辦法創(chuàng)建一個新的主隊列的。
以下是一個創(chuàng)建串行隊列的例子。
其中第一個參數(shù)隊列的label標記,第二個可以控制創(chuàng)建出來的隊列是串行隊列還是并行對列。
DISPATCH_QUEUE_SERIAL其實就是一個宏,實際上是NULL。
這也就是為什么,我們經(jīng)常會看到別人是這樣創(chuàng)建串行隊列的:
1.2創(chuàng)建并發(fā)隊列
第一種創(chuàng)建并發(fā)隊列的方式就是將上面方法的第二個參數(shù)修改為并發(fā)參數(shù):
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_CONCURRENT);第二種方式創(chuàng)建全局并發(fā)隊列:
dispatch_queue_t concurrent_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);該方法第一個參數(shù)是創(chuàng)建的queue的級別。有四個宏,分別對應(yīng)不同的級別:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 #define DISPATCH_QUEUE_PRIORITY_LOW (-2) #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN第二個參數(shù),是蘋果留作以后備用的,一般默認就填0就OK了。
因為DISPATCH_QUEUE_PRIORITY_DEFAULT其實就是等于0,所以我們常常會看到:
2 同步和異步
同步函數(shù)就是等待任務(wù)的執(zhí)行,等任務(wù)執(zhí)行完成后再返回。所以同步就意味著在當(dāng)前線程中執(zhí)行任務(wù),并不會開啟新的線程,不管是串行隊列還是并發(fā)隊列。
dispatch_sync(concurrent_queue, ^{ NSLog(@"打印%d----線程:%@", i,[NSThread currentThread]); //在當(dāng)前線程中執(zhí)行 });而異步函數(shù)就不會等待任務(wù)的執(zhí)行完成,它會立即返回。所以異步也就意味著會開啟一個新的線程,所以并不會阻塞當(dāng)前的線程。
dispatch_async(concurrent_queue, ^{ NSLog(@"打印%d----線程:%@", i,[NSThread currentThread]); //在新的線程中執(zhí)行 });3 總結(jié)
GCD的多線程使用都是同步/異步 與串行隊列/并發(fā)隊列組合使用的。
| 串行隊列 | 不創(chuàng)建新線程,順序執(zhí)行 |
| 并發(fā)隊列 | 不創(chuàng)建新線程,順序執(zhí)行 |
串行隊列無論是同步的執(zhí)行任務(wù),還是異步的執(zhí)行任務(wù),任務(wù)都是順序執(zhí)行的。
串行隊列同步任務(wù):
dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);for (int i = 0 ; i < 5; i++) {dispatch_sync(serial_queue, ^{NSLog(@"串行%d----線程:%@", i,[NSThread currentThread]);});}dispatch_sync(serial_queue, ^{NSLog(@"串行最后----線程:%@",[NSThread currentThread]);}); // 打印結(jié)果: 2016-07-08 16:15:13.421 PractiseProject[9528:187413] 串行0----線程:<NSThread: 0x7f93da408fe0>{number = 1, name = main} 2016-07-08 16:15:13.421 PractiseProject[9528:187413] 串行1----線程:<NSThread: 0x7f93da408fe0>{number = 1, name = main} 2016-07-08 16:15:13.422 PractiseProject[9528:187413] 串行2----線程:<NSThread: 0x7f93da408fe0>{number = 1, name = main} 2016-07-08 16:15:13.422 PractiseProject[9528:187413] 串行3----線程:<NSThread: 0x7f93da408fe0>{number = 1, name = main} 2016-07-08 16:15:13.422 PractiseProject[9528:187413] 串行4----線程:<NSThread: 0x7f93da408fe0>{number = 1, name = main} 2016-07-08 16:15:13.422 PractiseProject[9528:187413] 串行最后----線程:<NSThread: 0x7f93da408fe0>{number = 1, name = main}* 串行隊列異步任務(wù):*
dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);for (int i = 0 ; i < 5; i++) {dispatch_async(serial_queue, ^{NSLog(@"串行%d----線程:%@", i,[NSThread currentThread]);});}dispatch_async(serial_queue, ^{NSLog(@"串行最后----線程:%@",[NSThread currentThread]);}); // 打印結(jié)果: 2016-07-08 16:16:34.852 PractiseProject[9545:188432] 串行0----線程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)} 2016-07-08 16:16:34.852 PractiseProject[9545:188432] 串行1----線程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)} 2016-07-08 16:16:34.853 PractiseProject[9545:188432] 串行2----線程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)} 2016-07-08 16:16:34.853 PractiseProject[9545:188432] 串行3----線程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)} 2016-07-08 16:16:34.853 PractiseProject[9545:188432] 串行4----線程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)} 2016-07-08 16:16:34.853 PractiseProject[9545:188432] 串行最后----線程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)}* 串行隊列混合任務(wù): *
即使串行隊列中有同步任務(wù)和異步任務(wù),也還是按照添加任務(wù)的順序執(zhí)行,只不過同步任務(wù)在當(dāng)前線程執(zhí)行,異步任務(wù)在新線程中執(zhí)行:
* 并發(fā)隊列同步任務(wù):*
因為不會開啟新的線程,所以就在當(dāng)前線程中執(zhí)行,只有一條線程,因此只能順序執(zhí)行了。
* 并發(fā)隊列異步任務(wù):*
因為異步任務(wù)會開啟新的線程,所以哪個任務(wù)先執(zhí)行完畢,是不知道的。相同的示例代碼多次運行,任務(wù)執(zhí)行完成的順序是不一樣的。這里也可以看出一個GCD的優(yōu)點,它會復(fù)用之前使用過的閑置的線程。
* 并發(fā)隊列混合任務(wù):*
可以確定的是如果異步任務(wù)添加在同步任務(wù)后面,則一定會等同步任務(wù)執(zhí)行完畢后,才會執(zhí)行異步任務(wù);如果異步任務(wù)在同步任務(wù)前添加,則異步任務(wù)什么時候執(zhí)行完畢也是未知的,但是多個同步任務(wù)一定是順序執(zhí)行的。
注意事項
利用同步/異步任務(wù) 和串行隊列/并發(fā)隊列時,需要注意一些情況防止發(fā)生死鎖。
* 情形一*
在主線程中調(diào)度主隊列完成一個同步任務(wù),會發(fā)生死鎖。
如上代碼,界面永遠不會加載出來,里面的NSLog永遠也不會執(zhí)行。原因是ViewDidLoad是在主隊列的主線程中執(zhí)行,執(zhí)行到dispatch_sync 時會阻塞住,等待dispatch_sync中的打印任務(wù)執(zhí)行完畢。而dispatch_sync又會等viewDidLoad執(zhí)行完畢,再開始執(zhí)行,因此就互相等待發(fā)生死鎖。
* 情形二 *
在串行隊列的同步任務(wù)中再執(zhí)行同步任務(wù),會發(fā)生死鎖。
上面示例中的NSLog(@"串行1----線程:%@",[NSThread currentThread]);會打印,
但是NSLog(@"串行2----線程:%@",[NSThread currentThread]);永遠也不會執(zhí)行。
因為串行隊列一次只能執(zhí)行一個任務(wù),執(zhí)行完畢返回后,才會執(zhí)行下一個任務(wù),而外層任務(wù)的完成需要等待內(nèi)層任務(wù)的結(jié)束,而內(nèi)層任務(wù)的開始需要等外層任務(wù)結(jié)束。
其實情形一是情形二的一種特殊情況。
* 情形三 *
在串行隊列的異步任務(wù)中再嵌套執(zhí)行同步任務(wù),也會發(fā)生死鎖。
同樣的,由于串行隊列一次只能執(zhí)行一個任務(wù),任務(wù)結(jié)束后,才能執(zhí)行下一個任務(wù)。
所以異步任務(wù)的結(jié)束需要等里面同步任務(wù)結(jié)束,而里面同步任務(wù)的開始需要等外面異步任務(wù)結(jié)束,所以就相互等待,發(fā)生死鎖了。
第一篇就到這里了,下一篇記錄GCD的其他API。Have Fun!
轉(zhuǎn)載于:https://www.cnblogs.com/wanghang/p/6298855.html
總結(jié)
以上是生活随笔為你收集整理的GCD API 理解 (一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET小细节
- 下一篇: html5晋级之路-css背景