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,有需要的朋友可以去下載,別忘了給我個星星 - -,有問題可以隨時留言.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容

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