iOS GCD 從入門到放棄

一. 重點:

1.dispatch_queue_create(生成Dispatch Queue)

2.Main Dispatch Queue/Global Dispatch Queue

3.dispatch_set_target_queue

4.dispatch_after

5.Dispatch Group

6.dispatch_barrier_async

7.dispatch_sync

8.dispatch_apply

9.dispatch_suspend/dispatch_resume

10.Dispatch Semaphore

11.dispatch_once

12.Dispatch I/O

13.spatch_set_context與dispatch_set_finalizer_f

二.內容

#pragma mark -- 1.dispatch_queue_create(生成Dispatch Queue)

/*

1.Dispatch_Queue

兩種:

Serial Dispatch Queue? 串行隊列 順序執行

Concurrent Dispatch Queue 并行隊列 并行執行

dispatch_async(dispatch_queue_t queue>, ^(void)block)

*/


#pragma mark --? 2.Main Dispatch Queue/Global Dispatch Queue

/*

2.Dispatch_queue_creat(生成Dispatch Queue)

生成Serial Queue串行 但將4個它可并行實施多線程更新數據 每一個Searial Queue一個線程

1).生成Serial Queue

dispatch_queue_t queue = dispatch_queue_create("name", NULL);

2).生成Concurrent Queue

dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_CONCURRENT);

create 對應 release (MRC)

dispatch_release(queue);

*/

#pragma mark --? 3.dispatch_set_target_queue

//3.Main Dispatch Queue (串行)/ Global Dispatch Queue(并行) -- (系統的)

//1).Main Dispatch Queue 系統的主線程,串行線程

dispatch_queue_t mainQueue = dispatch_get_main_queue();

//2).Global Dispatch Queue(四種優先級) 不需要retain release

//(1)High Priority

dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

//(2)Default Priority

dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//(3)Low Priority

dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

//(4)Background Priority

dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

//在Main Dispatch Queue和Global Dispatch Queue

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 可并行執行處理

// 在Main Dispatch Queue中執行Block

dispatch_async(dispatch_get_main_queue(), ^{

// 主線程中執行處理

});

});

//數據的處理都放到并行線程中,對于UI的修改,要放到主線程中.

//4.dispatch_set_target_queue(變更生成的Dispatch Queue優先級)

dispatch_queue_t queue = dispatch_queue_create("name", NULL);

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

// 使生成的queue優先級與globalQueue相同

dispatch_set_target_queue(queue, globalQueue);

//層次管理

//Dispatch Queue 如果多個Serial Dispatch Queue使用該函數指定為目標為某一個Serial Dispatch Queue

#pragma mark --? 4.dispatch_after

//dispatch_after:是延遲提交,不是延遲運行, 同時并不精確

//官方文檔說明:Enqueue a block for execution at the specified time.

//Enqueue,就是入隊,指的就是將一個Block在特定的延時以后,加入到指定的隊列中,不是在特定的時間后立即運行!。

//方法一

/*

#define NSEC_PER_SEC 1000000000ull

#define USEC_PER_SEC 1000000ull

#define NSEC_PER_USEC 1000ull

- NSEC_PER_SEC,每秒有多少納秒。

- USEC_PER_SEC,每秒有多少毫秒。(注意是指在納秒的基礎上)

- NSEC_PER_USEC,每毫秒有多少納秒。

關鍵詞解釋:

- NSEC:納秒。

- USEC:微妙。

- SEC:秒

- PER:每

*/

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);

dispatch_after(time, dispatch_get_main_queue(), ^{

NSLog(@"處理事情");

});

//方法二

/*

dispatch_after 并不是在指定時間后執行處理,而是在指定質檢追加處理到Dispatch Queue于3秒后執行,dispatch_async()函數追到Block到Main Dispatch Queue

參數1:dispatch_time類型,使用dispatch_time函數和dispatch_walltime函數生成,dispatch_time類型值中指定的時間開始;dispatch_walltime計算絕對時間(固定時間)

參數2:指定要追加處理Dispatch Queue

參數3:指定要記述執行處理的Block

*/

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"處理事情");

});

//注意:

//創建串行隊列

dispatch_queue_t serialQueue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_SERIAL);

//立即打印一條信息

NSLog(@"開始添加block");

//提交一個block

dispatch_async(serialQueue, ^{

//讓線程沉睡10s

[NSThread sleepForTimeInterval:10];

NSLog(@"第一個block完成");

});

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)),serialQueue, ^{

NSLog(@"Afterblock完成");

});

/*

結果是: 開始添加block ->? 第一個block完成 ->? Afterblock完成

從結果也驗證了,dispatch_after只是延時提交block,并不是延時后立即執行。所以想用dispatch_after精確控制運行狀態的朋友可要注意了~

*/

#pragma mark --? 5.Dispatch Group 全部結束后想執行結束處理

//1) 三個事件異步執行,done在三個事件都完成之后,才會調用.

dispatch_group_t groupQueue = dispatch_group_create();

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_group_async(groupQueue, queue, ^{

NSLog(@"1");

});

dispatch_group_async(groupQueue, queue, ^{

NSLog(@"2");

});

dispatch_group_async(groupQueue, queue, ^{

NSLog(@"3");

});

dispatch_group_notify(groupQueue, dispatch_get_main_queue(), ^{

NSLog(@"done");

});

//2)等待其中并行處理多長時間之后,就執行

dispatch_group_t groupQueueTwo = dispatch_group_create();

dispatch_queue_t queueTwo = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_group_async(groupQueueTwo, queueTwo, ^{

NSLog(@"4");

});

dispatch_group_async(groupQueueTwo, queueTwo, ^{

NSLog(@"5");

});

dispatch_group_async(groupQueueTwo, queueTwo, ^{

NSLog(@"6");

});

//第二個參數指定為等待時間(超時)dispatch_time_t類型的值,上面用的是一直等待。

dispatch_group_wait(groupQueueTwo, DISPATCH_TIME_FOREVER);

dispatch_time_t queueTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));

long result = dispatch_group_wait(groupQueueTwo, queueTime);

if (!result) {

NSLog(@"屬于Dispatch Group全部處理執行結束");

}else

{

NSLog(@"屬于Dispatch Group的某一個處理還在執行");

}

/*

不為0意味著雖然過了指定時間,但屬于Dispatch Group的某一個處理還在執行中,為0全部處理執行結束

指定DISPATCH_TIME_NOW,則不用任何等待即可判定屬于Dispatch Group的處理是否結束,long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);

當你無法直接使用隊列變量時,就無法使用dispatch_group_async了,也可以使用dispatch_group_enter和dispatch_group_leave來作為判斷group結束的標志,下面以使用AFNetworking時的情況:

同時處理多個網絡請求,要求在所有請求都結束的時候,刷新UI,使用dispatch_group_enter,dispatch_group_leave就可以方便的將一系列網絡請求“打包”起來~

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

//Enter group

dispatch_group_enter(group);

[manager GET:@"http://www.baidu.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

//Deal with result...

//Leave group

dispatch_group_leave(group);

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

//Deal with error...

//Leave group

dispatch_group_leave(group);

}];

//More request...

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

更新UI

});

*/

#pragma mark --? 6.dispatch_barrier_async 用來加鎖,防止數據源沖突

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_async(queue, ^{

NSLog(@"11");

});

dispatch_async(queue, ^{

NSLog(@"12");

});

dispatch_async(queue, ^{

NSLog(@"13");

});

//執行barrier完成之后才繼續執行

dispatch_barrier_async(queue, ^{

NSLog(@"barrier done");

});

dispatch_async(queue, ^{

NSLog(@"14");

});

/*

dispatch_barrier_async的作用就是向某個隊列插入一個block,當目前正在執行的block運行完成后,阻塞這個block后面添加的block,只運行這個block直到完成,然后再繼續后續的任務,有點“唯我獨尊”的感覺=。=

值得注意的是:

dispatchbarrier\(a)sync只在自己創建的并發隊列上有效,在全局(Global)并發隊列、串行隊列上,效果跟dispatch_(a)sync效果一樣。

既然在串行隊列上跟dispatch_(a)sync效果一樣,那就要小心別死鎖!

dispatch_barrier_sync(dispatch_get_main_queue(), ^{

NSLog(@"我是死鎖");

});

*/

#pragma mark --? 7.dispatch_sync 同步(將Block同步到Dispatch Queue)中,在追加Block結束之前,dispatch_sync會一直等待(當前線程提醒)

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

// 處理結束前不會返回

dispatch_sync(queue, ^{

NSLog(@"done");

});

//dispatch_sync導致的死鎖

//涉及到多線程的時候,不可避免的就會有“死鎖”這個問題,在使用GCD時,往往一不小心,就可能造成死鎖,看看下面的“死鎖”例子:

/*

//在main線程使用“同步”方法提交Block,必定會死鎖。

dispatch_sync(dispatch_get_main_queue(), ^{

NSLog(@"I am block...");

});

*/

#pragma mark --? 8.dispatch_apply? 等待處理結果執行完才進行,關聯dispatch_sync函數和Dispatch Group的關聯API

/*

第一個參數為重復次數

第二個參數為追加對象的Dispatch Queue

第三個參數為追加處理 帶參數Block,為了區別重復Block

*/

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_apply(10, queue, ^(size_t index) {

NSLog(@"%zu",index);

});

//明明是提交到異步的隊列去運行,但是“After apply”居然在apply后打印,也就是說,dispatch_apply將外面的線程(main線程)“阻塞”了!

//查看官方文檔,dispatch_apply確實會“等待”其所有的循環運行完畢才往下執行=。=,看來要小心使用了。

/*

//在apply中使用主線程,會產生相互等待的死鎖

dispatch_apply(10, dispatch_get_main_queue(), ^(size_t index) {

NSLog(@"%zu",index);

});

避免dispatch_apply的嵌套調用,否則也會產生死鎖.

dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);

dispatch_apply(3, queue, ^(size_t i) {

NSLog(@"apply loop: %zu", i);

//再來一個dispatch_apply!死鎖!

dispatch_apply(3, queue, ^(size_t j) {

NSLog(@"apply loop inside %zu", j);

});

});

*/

NSLog(@"apply完成");

#pragma mark --? 9.dispatch_suspend/dispatch_resume

/*

掛起函數:dispatch_suspend(queue); 被追加到Dispatch Queue中的尚未執行的處理停止

回復函數:dispatch_resume(queue)p; 繼續執行

dispatch_suspend != 立即停止隊列的運行

dispatch_suspend,dispatch_resume提供了“掛起、恢復”隊列的功能,簡單來說,就是可以暫停、恢復隊列上的任務。但是這里的“掛起”,并不能保證可以立即停止隊列上正在運行的block,看如下例子:

*/

dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);

//提交第一個block 延時5s打印

dispatch_async(queue, ^{

[NSThread sleepForTimeInterval:5];

NSLog(@"After 5 seconds...");

});

//提交第二個block,也是延時5秒打印

dispatch_async(queue, ^{

[NSThread sleepForTimeInterval:5];

NSLog(@"After 5 seconds again...");

});

//延時一秒

NSLog(@"sleep 1 second...");

[NSThread sleepForTimeInterval:1];

//掛起隊列

NSLog(@"suspend...");

dispatch_suspend(queue);

//延時10秒

NSLog(@"sleep 10 second...");

[NSThread sleepForTimeInterval:10];

//恢復隊列

NSLog(@"resume...");

dispatch_resume(queue);

/*

2016-06-15 22:30:15.905 FJSGCDDemo[20425:462101] sleep 1 second...

2016-06-15 22:30:16.906 FJSGCDDemo[20425:462101] suspend...

2016-06-15 22:30:16.906 FJSGCDDemo[20425:462101] sleep 10 second...

2016-06-15 22:30:20.907 FJSGCDDemo[20425:462215] After 5 seconds...

2016-06-15 22:30:26.908 FJSGCDDemo[20425:462101] resume...

2016-06-15 22:30:31.913 FJSGCDDemo[20425:462143] After 5 seconds again...

可知,在dispatch_suspend掛起隊列后,第一個block還是在運行,并且正常輸出。

結合文檔,我們可以得知,dispatch_suspend并不會立即暫停正在運行的block,而是在當前block執行完成后,暫停后續的block執行。

所以下次想暫停正在隊列上運行的block時,還是不要用dispatch_suspend了吧~

*/

#pragma mark --? 10.Dispatch Semaphore 暫停,播放

/*

10.Dispatch Semaphore:暫停,播放

持有計數的信號,該計數是多線程中的計數類型信號,信號類似于過馬路手旗,可以通過舉起手旗,不可通過時放下手旗,計數為0時等待,計數為1或大于1時,減去1而不等待

*/

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

for (NSInteger i = 0; i < 10000; i++) {

dispatch_async(queue, ^{

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

// 進行排他處理,計數值+1進行

if (i == 500) {

dispatch_semaphore_signal(semaphore);

NSLog(@"我這個時候才能處理事情");

}

NSLog(@"難道我處理不了事情?");

});

}

#pragma mark --? 11.dispatch_once

//dispatch_once_t必須是全局或static變量

//這一條算是“老生常談”了,但我認為還是有必要強調一次,畢竟非全局或非static的dispatch_once_t變量在使用時會導致非常不好排查的bug,正確的如下:

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

// 初始化,一般用于單例中

});

#pragma mark --? 12.Dispatch I/O

//將文件分割成一塊一塊進行讀取

#pragma mark --? 13.spatch_set_context與dispatch_set_finalizer_f

/*

dispatch_set_context可以為隊列添加上下文數據,但是因為GCD是C語言接口形式的,所以其context參數類型是“void *”。也就是說,我們創建context時有如下幾種選擇:

用C語言的malloc創建context數據。

用C++的new創建類對象。

用Objective-C的對象,但是要用__bridge等關鍵字轉為Core Foundation對象。

以上所有創建context的方法都有一個必須的要求,就是都要釋放內存!,無論是用free、delete還是CF的CFRelease,我們都要確保在隊列不用的時候,釋放context的內存,否則就會造成內存泄露。

所以,使用dispatch_set_context的時候,最好結合dispatch_set_finalizer_f使用,為隊列設置“析構函數”,在這個函數里面釋放內存,大致如下:

void cleanStaff(void *context) {

//釋放context的內存!

//CFRelease(context);

//free(context);

//delete context;

}

...

//在隊列創建后,設置其“析構函數”

dispatch_set_finalizer_f(queue, cleanStaff);

*/

附上github地址https://github.com/BestJoker/FJSGCDDemo.git,有需要的朋友可以去下載,別忘了給我個星星 - -,有問題可以隨時留言.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 簡介 GCD(Grand Central Dispatch)是在macOS10.6提出來的,后來在iOS4.0被引...
    sunmumu1222閱讀 895評論 0 2
  • 本篇博客共分以下幾個模塊來介紹GCD的相關內容: 多線程相關概念 多線程編程技術的優缺點比較? GCD中的三種隊列...
    dullgrass閱讀 37,897評論 30 236
  • iOS中GCD的使用小結 作者dullgrass 2015.11.20 09:41*字數 4996閱讀 20199...
    DanDanC閱讀 890評論 0 0
  • 本篇博客共分以下幾個模塊來介紹GCD的相關內容: 多線程相關概念 多線程編程技術的優缺點比較? GCD中的三種隊列...
    有夢想的老伯伯閱讀 1,031評論 0 4
  • 我們知道在iOS開發中,一共有四種多線程技術:pthread,NSThread,GCD,NSOperation: ...
    請叫我周小帥閱讀 1,511評論 0 1