iOS多線程實現3-GCD,ios多線程3-gcd
敲下gcd三個字母,搜狗第一條顯示居然是“滾床單” ^_^
一、介紹
GCD,英文全稱是Grand Central Dispatch(功能強悍的中央調度器),基於C語言編寫的一套多線程開發機制,因此使用時會以函數形式出現,且大部分函數以dispatch開頭,雖然是C語言的但相對於蘋果其它多線程實現方式,抽象層次更高,使用起來也更加方便。
它是蘋果為應對多核的並行運算提出的解決方案,它會自動利用多核進行並發處理和運算,且線程由系統自動管理(調度、運行),無需程式員參與,使用起來非常方便。
二、任務和隊列
GCD有兩個核心:任務和隊列。
任務:要執行的操作或方法函數,隊列:存放任務的集合,而我們要做的就是將任務添加到隊列然後執行,GCD會自動將隊列中的任務按先進先出的方式取出並交給對應線程執行。注意任務的取出是按照先進先出的方式,這也是隊列的特性,但是取出後的執行順序則不一定,下面會詳細討論。
1 任務
可以簡單的認為是一個操作,一個函數,一個方法等等,在實際的開發中大多是以block的形式,使用起來也更加靈活。block使用詳見:http://www.cnblogs.com/mddblog/p/4754190.html
2 隊列
有兩種隊列:串列隊列和並行隊列。串列隊列:同步執行,在當前線程執行;並行隊列:可由多個線程非同步執行,但任務的取出還是FIFO的。
隊列建立,根據函數第二個參數來建立串列或並行隊列。
// 參數1 隊列名稱// 參數2 隊列類型 DISPATCH_QUEUE_SERIAL/NULL串列隊列,DISPATCH_QUEUE_CONCURRENT代表並行隊列// 下面代碼為建立一個串列隊列,也是實際開發中用的最多的dispatch_queue_t serialQ = dispatch_queue_create("隊列名", NULL);
另外系統提供了兩種隊列:全域隊列和主隊列。
全域隊列屬於並行隊列,只不過已由系統建立的沒有名字,且在全域可見(可用)。擷取全域隊列:
/* 取得全域隊列 第一個參數:線程優先順序,設為預設即可,個人習慣寫0,等同於預設 第二個參數:標記參數,目前沒有用,一般傳入0 */serialQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
主隊列屬於串列隊列,也由系統建立,只不過運行在主線程(UI線程)。擷取主隊列:
// 擷取主隊列serialQ = dispatch_get_main_queue();
3 執行方式-2種
同步執行和非同步執行。
同步執行:不會開啟線程,在當前線程執行。
非同步執行:gcd管理的線程池中有空閑線程就會從隊列中取出任務執行,會開啟線程。
下面為實現同步和非同步函數,函數功能為:將任務添加到隊列並執行。
/* 同步執行 第一個參數:執行任務的隊列:串列、並行、全域、主隊列 第二個參數:block任務 */void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);// 非同步執行void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
三、幾種類型
很明顯兩種執行方式,兩種隊列。那麼就有4種情況:串列隊列同步執行、串列隊列非同步執行、並行隊列同步執行、並行隊列非同步執行。哪一種會開啟新的線程?開幾條?是否並發?記憶起來比較繞,但是只要抓住基本的就可以,為了方便理解,現分析如下:
1)串列隊列,同步執行-----串列隊列意味著順序執行,同步執行意味著不開啟線程(在當前線程執行)
2)串列隊列,非同步執行-----串列隊列意味著任務順序執行,非同步執行說明要開線程, (如果開多個線程的話,不能保證串列隊列順序執行,所以只開一個線程)
3)並行隊列,非同步執行-----並行隊列意味著執行順序不確定,非同步執行意味著會開啟線程,而並行隊列又允許不按順序執行,所以系統為了提高效能會開啟多個線程,來隊列取任務(隊列中任務取出仍然是順序取出的,只是線程執行無序)。
4)並行隊列,同步執行-----同步執行意味著不開線程,則肯定是順序執行
5)主線程中主隊列,同步執行-----程式執行不出來(死結) ;原因:主隊列,如果主線程正在執行代碼,就不調度任務;同步執行:一直執行第一個任務直到結束。兩者互相等待造成死結。
// 在主線程執行下面代碼,會死結dispatch_sync(dispatch_get_main_queue(), ^{ // 要執行的代碼});
四、常用舉例
1 線程間通訊
比如,為了提高使用者體驗,我們一般在其他線程(非主線程)下載圖片或其它網路資源,下載完成後我們要更新UI,而UI更新必須在主線程執行,所以我們經常會使用:
// 同步執行,會阻塞指導下面block中的代碼執行完畢dispatch_sync(dispatch_get_main_queue(), ^{ // 主線程,UI更新});// 非同步執行dispatch_async(dispatch_get_main_queue(), ^{ // 主線程,UI更新});
2 其它常用
全域隊列,實現並發:
dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 要執行的代碼});
五、Dispatch Group調度組
使用調度組,可以輕鬆實現在一些任務完成後,做一些操作。比如具有順序性要求的生產者消費者等等。
樣本1 任務1完成之後執行任務2。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self groupTest];}- (void)groupTest { // 建立一個組 dispatch_group_t group = dispatch_group_create(); NSLog(@"開始執行"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ // 任務1 // 等待1s一段時間在執行 [NSThread sleepForTimeInterval:1]; NSLog(@"task1 running in %@",[NSThread currentThread]); }); dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{ // 任務2 NSLog(@"task2 running in %@",[NSThread currentThread]); }); });}
點擊螢幕後,列印如下,可以看到任務1雖然等待了1s,任務2也不執行,只有任務1執行完畢才執行任務2.
2015-08-28 18:16:05.317 GCDTest[1468:229374] 開始執行2015-08-28 18:16:06.323 GCDTest[1468:229457] task1 running in <NSThread: 0x7f8962f16900>{number = 2, name = (null)}2015-08-28 18:16:06.323 GCDTest[1468:229456] task2 running in <NSThread: 0x7f8962c92750>{number = 3, name = (null)}
樣本2,其實樣本1並不常用,真正用到的是監控多個任務完成之後,回到主線程更新UI,或者做其它事情。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self groupTest];}- (void)groupTest { // 建立一個組 dispatch_group_t group = dispatch_group_create(); NSLog(@"開始執行"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ // 關聯任務1 NSLog(@"task1 running in %@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ // 關聯任務2 NSLog(@"task2 running in %@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ // 關聯任務3 NSLog(@"task3 running in %@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ // 關聯任務4 // 等待1秒 [NSThread sleepForTimeInterval:1]; NSLog(@"task4 running in %@",[NSThread currentThread]); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 回到主線程執行 NSLog(@"mainTask running in %@",[NSThread currentThread]); }); });}
點擊螢幕後,列印如下,可以看到無論其它任務然後和執行,mainTask等待它們執行後才執行。
2015-08-28 18:24:14.312 GCDTest[1554:236273] 開始執行2015-08-28 18:24:14.312 GCDTest[1554:236352] task3 running in <NSThread: 0x7fa8f1f0c9c0>{number = 4, name = (null)}2015-08-28 18:24:14.312 GCDTest[1554:236354] task1 running in <NSThread: 0x7fa8f1d10750>{number = 2, name = (null)}2015-08-28 18:24:14.312 GCDTest[1554:236351] task2 running in <NSThread: 0x7fa8f1c291a0>{number = 3, name = (null)}2015-08-28 18:24:15.313 GCDTest[1554:236353] task4 running in <NSThread: 0x7fa8f1d0e7f0>{number = 5, name = (null)}2015-08-28 18:24:15.313 GCDTest[1554:236273] mainTask running in <NSThread: 0x7fa8f1c13df0>{number = 1, name = main}