iOS-GCD多執行緒程式設計詳解1
文明看博轉載是對自己的尊重也是對學者的鼓勵,歡迎討論
iOS-GCD多執行緒程式設計詳解
一.前言
前面的多執行緒程式設計中分別講到NSThread和NSOperation的多執行緒程式設計,本張主要是講述GCD的程式設計,GCD的多執行緒程式設計是基於BLock或者函式包裹多執行緒任務,由系統管理執行緒實現的,因此其低耦合,容易使用,又是基於C語言實現,執行速度快,是官方推薦的多執行緒實現的首先方案,至於個人什麼順手就用什麼。
二.GCD多執行緒使用說明
在多執行緒程式設計中,首先要考慮的問題是多執行緒任務寫在什麼地方,同步還是非同步,是在主執行緒中執行還是在子執行緒中執行,多執行緒任務的依賴關係,執行的先後順序,是自己建立執行緒還是系統的執行緒承載執行緒任務的執行,是自己管理執行緒還是系統管理,對於GCD而言,多執行緒任務通過Block或者函式來包含多執行緒任務,它的依賴和執行順序有特定的GCD函式,它的任務承載執行緒是有系統生成和管理,所以使用者只需要關心多執行緒任務是使用block還是函式包裹,是使用同步還是非同步,是在主執行緒上執行還是在子執行緒上執行。
1.dispathc_sync:
該函式的作用是提交一個block到指定的dispatch_queue中,直到block執行結束返回,在主執行緒中使用該函式就會造成UI卡住:
note:下面這段程式碼是在主執行緒中呼叫
執行結果:NSLog(@"before dispatch_sync");<span style="white-space:pre"> </span>//主執行緒中呼叫 dispatch_sync(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"hello world");<span style="white-space:pre"> </span>//這條語句沒有被執行 }); });
2015-01-03 17:45:58.213 GCDTest[1750:140657] before dispatch_sync(還有就是介面卡死了,因為形成了死鎖)
note:下面的這段程式碼是在非主執行緒中呼叫:
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(netThread:) toTarget:self withObject:@"new thread"]; //子執行緒分支 NSLog(@"hello world main"); } -(void)netThread:(id)sender{ NSLog(@"before dispatch_sync"); dispatch_sync(dispatch_get_main_queue(), ^{<span style="white-space:pre"> </span>//相當於在主執行緒中呼叫dispatch_sync dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"hello world subThread");<span style="white-space:pre"> </span>//從結果中看出這條語句沒有被執行 }); }); } @end
執行結果:
2015-01-04 17:37:04.426 GCDTest[1869:155469] before dispatch_sync(螢幕卡死)
2015-01-04 17:37:04.426 GCDTest[1869:155423] hello world main
所以在使用disatich_sync時不要在UI執行緒使用。
相似函式:
dispatch_sync_f:該函式提交的是一個函式任務,相當於dispatch_sync:提交block,說白了把提交的function相當於block,有興趣的可以看我其中的一篇BLock揭祕,瞭解函式和block的一些對映個人思想。
dispatch_async_f(dispatch_get_main_queue(), nil, fun_my);
}
static void fun_my(void *hel){
NSLog(@"function");
}
執行結果:
2015-01-04 00:53:48.894 GCDTest[2010:161173] function
在主執行緒中使用的dispatch_sync(dispatch_get_main_queue,^{})形成死鎖圖解:
{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"我是非同步的block1,我被裝在dispatch_get_main_queue中");
});
NSLog(@"我是主執行緒,我的原始碼位置被寫在block1的後面");
}
執行結果:
2015-01-04 22:37:31.821 GCDTest[713:28383] 我是主執行緒,我的原始碼位置被寫在block1的後面
2015-01-04 22:37:31.836 GCDTest[713:28383] 我是非同步的block1,我被裝在dispatch_get_main_queue中
圖解分析:
三.各個GCD函式使用詳解
通過上一節的簡單使用,我們可以把GCD分解為執行緒任務包裝:block,function,執行緒任務的容器:dispatch_queue(序列型別和併發型別),同步和非同步(重點是非同步的實現);block可以參考點選開啟連結,function就是一般的函式,同步和非同步就是同步執行和非同步執行,下面通過程式碼來了解block的容器-------dispatch_queue.
1.dispatch_global_queue
dispatch_group_t group2 = dispatch_group_create();
dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{
NSLog(@"*block1 執行線上程%@",[NSThread currentThread]);
});
dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{
NSLog(@"*block2 執行線上程%@",[NSThread currentThread]);
});
dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{
NSLog(@"*block3 執行線上程%@",[NSThread currentThread]);
});
dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{
NSLog(@"*block4 執行線上程%@",[NSThread currentThread]);
});
dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{
NSLog(@"*block5 執行線上程%@",[NSThread currentThread]);
});
執行結果:
1:
2015-01-04 23:04:37.273 GCDTest[850:36850] *block2 執行線上程<NSThread: 0x7fc2d9c6e350>{number = 5, name = (null)}
2015-01-04 23:04:37.273 GCDTest[850:36848] *block3 執行線上程<NSThread: 0x7fc2d9c6b200>{number = 3, name = (null)}
2015-01-04 23:04:37.273 GCDTest[850:36849] *block4 執行線上程<NSThread: 0x7fc2d9f269a0>{number = 4, name = (null)}
2015-01-04 23:04:37.274 GCDTest[850:36854] *block5 執行線上程<NSThread: 0x7fc2d9c6d180>{number = 6, name = (null)}
2015-01-04 23:04:37.273 GCDTest[850:36847] *block1 執行線上程<NSThread: 0x7fc2d9d1ce30>{number = 2, name = (null)}
2:2015-01-04 23:07:09.728 GCDTest[858:37701] *block2 執行線上程<NSThread: 0x7fc569406f40>{number = 4, name = (null)}
2015-01-04 23:07:09.728 GCDTest[858:37700] *block3 執行線上程<NSThread: 0x7fc56940da90>{number = 5, name = (null)}
2015-01-04 23:07:09.728 GCDTest[858:37698] *block1 執行線上程<NSThread: 0x7fc5694182a0>{number = 2, name = (null)}
2015-01-04 23:07:09.728 GCDTest[858:37699] *block4 執行線上程<NSThread: 0x7fc56952c740>{number = 3, name = (null)}
2015-01-04 23:07:09.728 GCDTest[858:37704] *block5 執行線上程<NSThread: 0x7fc569713820>{number = 6, name = (null)}
。。。執行結果每次都不同,所在的執行執行緒也不同,所以dispatch_global_queue是並行的系統維護的執行緒依次衝佇列中取出block執行
2.dispatch_main_queue
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_main_queue(), ^{
NSLog(@"block1 執行線上程%@",[NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_main_queue(), ^{
NSLog(@"block2 執行線上程%@",[NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_main_queue(), ^{
NSLog(@"block3 執行線上程%@",[NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_main_queue(), ^{
NSLog(@"block4 執行線上程%@",[NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_main_queue(), ^{
NSLog(@"block5 執行線上程%@",[NSThread currentThread]);
});
執行結果:
2015-01-04 23:07:09.762 GCDTest[858:37660] block1 執行線上程<NSThread: 0x7fc569613060>{number = 1, name = main}
2015-01-04 23:07:09.762 GCDTest[858:37660] block2 執行線上程<NSThread: 0x7fc569613060>{number = 1, name = main}
2015-01-04 23:07:09.762 GCDTest[858:37660] block3 執行線上程<NSThread: 0x7fc569613060>{number = 1, name = main}
2015-01-04 23:07:09.762 GCDTest[858:37660] block4 執行線上程<NSThread: 0x7fc569613060>{number = 1, name = main}
2015-01-04 23:07:09.763 GCDTest[858:37660] block5 執行線上程<NSThread: 0x7fc569613060>{number = 1, name = main}
執行結果每次都是一致的,說明dispatch_main_queue是根據其加入順序執行的執行的執行緒為主執行緒;所以順序佇列和並行佇列的對比圖如下:
小結:這篇博文主要說了關於順序佇列和並行佇列的block容器的特性,後面將會陸續的對GCD的多執行緒程式設計有詳細的使用。
引用或者轉載請註明出處,是對自己的尊重也是對學者的鼓勵。