日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

GCD API 理解 (一)

發(fā)布時間:2025/3/15 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GCD API 理解 (一) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

資料先行

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)建串行隊列的例子。

dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);

其中第一個參數(shù)隊列的label標記,第二個可以控制創(chuàng)建出來的隊列是串行隊列還是并行對列。
DISPATCH_QUEUE_SERIAL其實就是一個宏,實際上是NULL。
這也就是為什么,我們經(jīng)常會看到別人是這樣創(chuàng)建串行隊列的:

dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", NULL);
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,所以我們常常會看到:

dispatch_queue_t concurrent_queue = dispatch_get_global_queue(0, 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í)行:

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_async(serial_queue, ^{NSLog(@"串行最后----線程:%@",[NSThread currentThread]);}); // 打印結(jié)果: 2016-07-08 16:21:34.545 PractiseProject[9588:190084] 串行0----線程:<NSThread: 0x7fb283609b30>{number = 1, name = main} 2016-07-08 16:21:34.545 PractiseProject[9588:190084] 串行1----線程:<NSThread: 0x7fb283609b30>{number = 1, name = main} 2016-07-08 16:21:34.545 PractiseProject[9588:190084] 串行2----線程:<NSThread: 0x7fb283609b30>{number = 1, name = main} 2016-07-08 16:21:34.545 PractiseProject[9588:190084] 串行3----線程:<NSThread: 0x7fb283609b30>{number = 1, name = main} 2016-07-08 16:21:34.546 PractiseProject[9588:190084] 串行4----線程:<NSThread: 0x7fb283609b30>{number = 1, name = main} 2016-07-08 16:21:34.546 PractiseProject[9588:190120] 串行最后----線程:<NSThread: 0x7fb28362ee30>{number = 2, name = (null)}

* 并發(fā)隊列同步任務(wù):*
因為不會開啟新的線程,所以就在當(dāng)前線程中執(zhí)行,只有一條線程,因此只能順序執(zhí)行了。

dispatch_queue_t concurrent_queue = dispatch_get_global_queue(0, 0);for (int i = 0 ; i < 5; i++) {dispatch_sync(concurrent_queue, ^{NSLog(@"并行%d----線程:%@", i,[NSThread currentThread]);});}dispatch_sync(concurrent_queue, ^{NSLog(@"并行最后----線程:%@",[NSThread currentThread]);}); //打印結(jié)果: 2016-07-08 16:26:44.264 PractiseProject[9671:193541] 并發(fā)0----線程:<NSThread: 0x7f905a404a00>{number = 1, name = main} 2016-07-08 16:26:44.264 PractiseProject[9671:193541] 并發(fā)1----線程:<NSThread: 0x7f905a404a00>{number = 1, name = main} 2016-07-08 16:26:44.265 PractiseProject[9671:193541] 并發(fā)2----線程:<NSThread: 0x7f905a404a00>{number = 1, name = main} 2016-07-08 16:26:44.265 PractiseProject[9671:193541] 并發(fā)3----線程:<NSThread: 0x7f905a404a00>{number = 1, name = main} 2016-07-08 16:26:44.265 PractiseProject[9671:193541] 并發(fā)4----線程:<NSThread: 0x7f905a404a00>{number = 1, name = main} 2016-07-08 16:26:44.265 PractiseProject[9671:193541] 并發(fā)最后----線程:<NSThread: 0x7f905a404a00>{number = 1, name = main}

* 并發(fā)隊列異步任務(wù):*
因為異步任務(wù)會開啟新的線程,所以哪個任務(wù)先執(zhí)行完畢,是不知道的。相同的示例代碼多次運行,任務(wù)執(zhí)行完成的順序是不一樣的。這里也可以看出一個GCD的優(yōu)點,它會復(fù)用之前使用過的閑置的線程。

dispatch_queue_t concurrent_queue = dispatch_get_global_queue(0, 0);for (int i = 0 ; i < 5; i++) {dispatch_async(concurrent_queue, ^{NSLog(@"并行%d----線程:%@", i,[NSThread currentThread]);});}dispatch_async(concurrent_queue, ^{NSLog(@"并行最后----線程:%@",[NSThread currentThread]);}); // 打印結(jié)果: 2016-07-08 16:29:03.522 PractiseProject[9696:194688] 并發(fā)1----線程:<NSThread: 0x7f8020c2bee0>{number = 4, name = (null)} 2016-07-08 16:29:03.522 PractiseProject[9696:194694] 并發(fā)0----線程:<NSThread: 0x7f8020d1ec90>{number = 3, name = (null)} 2016-07-08 16:29:03.522 PractiseProject[9696:194703] 并發(fā)3----線程:<NSThread: 0x7f8020e153e0>{number = 6, name = (null)} 2016-07-08 16:29:03.522 PractiseProject[9696:194693] 并發(fā)2----線程:<NSThread: 0x7f8020c2bf20>{number = 5, name = (null)} 2016-07-08 16:29:03.523 PractiseProject[9696:194688] 并發(fā)4----線程:<NSThread: 0x7f8020c2bee0>{number = 4, name = (null)} 2016-07-08 16:29:03.523 PractiseProject[9696:194694] 并發(fā)最后----線程:<NSThread: 0x7f8020d1ec90>{number = 3, name = (null)}

* 并發(fā)隊列混合任務(wù):*
可以確定的是如果異步任務(wù)添加在同步任務(wù)后面,則一定會等同步任務(wù)執(zhí)行完畢后,才會執(zhí)行異步任務(wù);如果異步任務(wù)在同步任務(wù)前添加,則異步任務(wù)什么時候執(zhí)行完畢也是未知的,但是多個同步任務(wù)一定是順序執(zhí)行的。

dispatch_queue_t concurrent_queue = dispatch_get_global_queue(0, 0);for (int i = 0 ; i < 5; i++) {dispatch_sync(concurrent_queue, ^{NSLog(@"并行%d----線程:%@", i,[NSThread currentThread]);});}dispatch_async(concurrent_queue, ^{NSLog(@"并行最后----線程:%@",[NSThread currentThread]);});for (int i = 0 ; i < 5; i++) {dispatch_sync(concurrent_queue, ^{NSLog(@"并行%d----線程:%@", i,[NSThread currentThread]);});} // 打印結(jié)果(多次運行打印結(jié)果并不相同) 2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并發(fā)0----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并發(fā)1----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并發(fā)2----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并發(fā)3----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并發(fā)4----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并發(fā)0----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并發(fā)1----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并發(fā)2----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.274 PractiseProject[9805:199470] 并發(fā)最后----線程:<NSThread: 0x7f943362ffc0>{number = 3, name = (null)} 2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并發(fā)3----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main} 2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并發(fā)4----線程:<NSThread: 0x7f9433408d40>{number = 1, name = main}

注意事項

利用同步/異步任務(wù) 和串行隊列/并發(fā)隊列時,需要注意一些情況防止發(fā)生死鎖。
* 情形一*
在主線程中調(diào)度主隊列完成一個同步任務(wù),會發(fā)生死鎖。

- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.self.view.backgroundColor = [UIColor orangeColor];dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"串行----線程:%@",[NSThread currentThread]);}); }

如上代碼,界面永遠不會加載出來,里面的NSLog永遠也不會執(zhí)行。原因是ViewDidLoad是在主隊列的主線程中執(zhí)行,執(zhí)行到dispatch_sync 時會阻塞住,等待dispatch_sync中的打印任務(wù)執(zhí)行完畢。而dispatch_sync又會等viewDidLoad執(zhí)行完畢,再開始執(zhí)行,因此就互相等待發(fā)生死鎖。
* 情形二 *
在串行隊列的同步任務(wù)中再執(zhí)行同步任務(wù),會發(fā)生死鎖。

dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);dispatch_sync(serial_queue, ^{NSLog(@"串行1----線程:%@",[NSThread currentThread]);dispatch_sync(serial_queue, ^{NSLog(@"串行2----線程:%@",[NSThread currentThread]);});});

上面示例中的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ā)生死鎖。

dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);dispatch_async(serial_queue, ^{NSLog(@"串行異步----線程:%@",[NSThread currentThread]);dispatch_sync(serial_queue, ^{NSLog(@"串行2----線程:%@",[NSThread currentThread]);});[NSThread sleepForTimeInterval:2.0];});

同樣的,由于串行隊列一次只能執(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。