GCD

0. Description
  • iOS SDK >= 6.0且开启ARC,GCD对象就不应该使用dispatch_retain和dispatch_release了(全局队列任何时候都不需要retain、release),GCD对象可用strong修饰。
  • Concurrency & Parallelism
    并发的意思就是同时运行多个任务,这些任务可能是在单核 CPU 上以分时(时间共享)的形式同时运行,也可能是在多核 CPU 上以真正的并行方式来运行。然后为了使单核设备也能实现这一点,并发任务必须先运行一个线程,执行一个上下文切换,然后运行另一个线程或进程。并行则是真正意义上的多任务同时运行。
  • Context Switch
    上下文切换,一个上下文切换指当你在单个进程里切换执行不同的线程时存储与恢复执行状态的过程。这个过程在编写多任务应用时很普遍,但会带来一些额外的开销。
1. Queue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t customSerialQueue = dispatch_queue_create("customSerialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t customConcurrentQueue = dispatch_queue_create("customConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
2. Normal usage
  • 2.1 sync mainQueue
//sync mainQueue : 阻塞当前线程,block在主线程中执行,执行结束后返回。
    dispatch_sync(mainQueue, ^{
        NSLog(@"sync mainQueue %@", [NSThread currentThread]);
    });
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//死锁情况1~~使用sync时,当前queue和执行queue相同会造成死锁,并行queue不会
//死锁情况2~~在死锁1的情况下嵌套一个sync,同样会发生死锁,如下:
    dispatch_sync(customSerialQueue或者globalQueue或者customConcurrentQueue, ^{ //嵌套的sync
        dispatch_sync(mainQueue, ^{
            NSLog(@"sync mainQueue %@", [NSThread currentThread]);
        });
    });
  • 2.2 async mainQueue
//async mainQueue : 不阻塞当前线程,直接返回,block在主线程中执行
    dispatch_async(mainQueue, ^{
        NSLog(@"async mainQueue %@", [NSThread currentThread]);
    });
  • 2.3 sync customSerialQueue
//sync customSerialQueue : 阻塞当前线程,block在当前线程中执行,执行结束后返回。
    dispatch_sync(customSerialQueue, ^{
        NSLog(@"sync customSerialQueue %@", [NSThread currentThread]);
    });
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//死锁情况1~~使用sync时,当前queue和执行queue相同会造成死锁,并行queue不会
//死锁情况2~~在死锁1的情况下嵌套一个sync,同样会发生死锁,如下:
    dispatch_async(customSerialQueue, ^{
        dispatch_sync(mainQueue或者globalQueue或者customConcurrentQueue, ^{  //嵌套的sync
            dispatch_sync(customSerialQueue, ^{
                NSLog(@"sync customSerialQueue %@", [NSThread currentThread]);
            });
        });
    });
  • 2.4 async customSerialQueue
//async customSerialQueue : 不阻塞当前线程,直接返回,block在一个新的线程中执行。(如果指定同一个customSerialQueue,则只会在唯一的新线程中执行;如果指定不同的customSerialQueue,则会生成多个新线程)
    dispatch_async(customSerialQueue, ^{
        NSLog(@"async customSerialQueue %@", [NSThread currentThread]);
    });
  • 2.5 sync globalQueue
//sync globalQueue : 阻塞当前线程,block在当前线程中执行,执行结束后返回。
    dispatch_sync(globalQueue, ^{
        NSLog(@"sync globalQueue %@", [NSThread currentThread]);
    });
  • 2.6 async globalQueue
//async globalQueue : 不阻塞当前线程,直接返回,block每次都在一个新的线程中执行
    dispatch_async(globalQueue, ^{
        NSLog(@"async globalQueue %@", [NSThread currentThread]);
    });
  • 2.7 sync customConcurrentQueue
//sync customConcurrentQueue : 阻塞当前线程,block在当前线程中执行,执行结束后返回。
    dispatch_sync(customConcurrentQueue, ^{
        NSLog(@"sync customConcurrentQueue %@", [NSThread currentThread]);
    });
  • 2.8 async customConcurrentQueue
//async customConcurrentQueue : 不阻塞当前线程,直接返回,block每次都在一个新的线程中执行
    dispatch_async(customConcurrentQueue, ^{
        NSLog(@"async customConcurrentQueue %@", [NSThread currentThread]);
    });
3. Dispatch group

如果要在queue中的所有任务都结束后做某件事情,由于除去2.6、2.8以外的方式都在单一的线程中执行任务,所以把这件任务最后添加即可。2.6、2.8的方式是在多个线程中执行任务,为了达到同步的目的就可以使用Dispatch group。

  • 3.1 异步等待任务完成
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t cQueue = dispatch_queue_create("cQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, cQueue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"1 %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, cQueue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"2 %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, cQueue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"3 %@", [NSThread currentThread]);
    });
//dispatch_group_notify 异步等待任务完成
//notifyQueue为mainQueue则block在主线程中执行,notifyQueue为其他则block在非主线程中执行
    dispatch_group_notify(group, notifyQueue, ^{
        NSLog(@"4 %@", [NSThread currentThread]);
    });
//运行结果:1 3 2 4
  • 3.2 阻塞当前线程等待
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t cQueue = dispatch_queue_create("cQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, cQueue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"1 %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, cQueue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"2 %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, cQueue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"3 %@", [NSThread currentThread]);
    });
    
    //dispatch_group_wait 阻塞当前线程,阻塞时间在规定时间和任务执行时间中取最小值,但无论如何都会完成所有任务
    long wt = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC));
    if(wt == 0) {
        NSLog(@"group中任务执行时间不到规定时间");
    }
    else {
        NSLog(@"group中任务执行时间超过规定时间");
    }
//运行结果:如果规定时间为2秒,则为1 group中任务执行时间超过规定时间 3 2;
            如果规定时间为5秒,则为1 3 2 group中任务执行时间不到规定时间
  • 3.3 dispatch_group_async的等价实现
    以下两种实现是等价的
    dispatch_group_async(group, cQueue, ^{
    });
    dispatch_group_enter(group);
    dispatch_async(cQueue, ^{
        dispatch_group_leave(group);
    });

dispatch_group_enter()和dispatch_group_leave()需要成对出现。

4. Dispatch block
  • 4.1 dispatch_block_t
    sync customSerialQueue的例子
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"do something");
    });
    
    dispatch_sync(customSerialQueue, block);
  • 4.2 dispatch_block_wait
    会阻塞当前线程,阻塞时间在规定时间和block执行时间中取最小值,但无论如何都会完成所有任务。
    dispatch_queue_t customSerialQueue = dispatch_queue_create("customSerialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t block = dispatch_block_create(0, ^{
        [NSThread sleepForTimeInterval:3];
    });
    dispatch_async(customSerialQueue, block);
    
    long wt = dispatch_block_wait(block, dispatch_time(DISPATCH_TIME_NOW, 4 * NSEC_PER_SEC));
    if(wt == 0)
    {
        NSLog(@"block执行时间不到规定时间");
    }
    else
    {
        NSLog(@"block执行时间超过规定时间");
    }

将DISPATCH_TIME_NOW替换成DISPATCH_TIME_FOREVER,则会一直等待,不会超时。

  • 4.3 dispatch_block_notify
    dispatch_queue_t customConcurrentQueue = dispatch_queue_create("customConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_block_t previousBlock = dispatch_block_create(0, ^{
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(customConcurrentQueue, previousBlock);
    
    dispatch_block_t notifyBlock = dispatch_block_create(0, ^{
        NSLog(@"previousBlock done");
    });
    //dispatch_block_notify 异步
    //当previousBlock执行完毕后,提交notifyBlock到notifyQueue中执行,notifyQueue为mainQueue则block在主线程中执行,notifyQueue为其他则block在非主线程中执行
    dispatch_block_notify(previousBlock, notifyQueue, notifyBlock);
  • 4.4 dispatch_block_cancel
    可以取消提交到队列的block,对已经开始执行的无效
    dispatch_async(someQueue, block);
    dispatch_block_cancel(block);
5. dispatch_after

异步非阻塞,延迟执行

 dispatch_time_t disTime = dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC);
 //queue为mainQueue在主线程中执行,其他在非主线程中执行
 dispatch_after(disTime, queue, ^{
     NSLog(@"%@", [NSThread currentThread]);
 });
6. dispatch_apply

dispatch_apply为同步会阻塞线程,会将block重复执行n次。

  • 6.1 在mainQueue中执行
//block都在主线程中执行
    dispatch_apply(4, mainQueue, ^(size_t i) {
        [NSThread sleepForTimeInterval:(4-i)];
        NSLog(@"%zu %@", i, [NSThread currentThread]);
    });
    NSLog(@"done");
//运行结果:0 1 2 3 done
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//死锁情况1~~如果当前queue和执行queue都是mainQueue会造成死锁
//死锁情况2~~在死锁1的情况下嵌套一个sync,同样会发生死锁,如下:
    dispatch_sync(customSerialQueue或者globalQueue或者customConcurrentQueue, ^{  //嵌套的sync
        dispatch_apply(4, mainQueue, ^(size_t i) {
            [NSThread sleepForTimeInterval:(4-i)];
            NSLog(@"%zu %@", i, [NSThread currentThread]);
        });
        NSLog(@"done");
    });
  • 6.2 在customSerialQueue中执行
//block都在当前线程中执行
    dispatch_apply(4, customSerialQueue, ^(size_t i) {
        [NSThread sleepForTimeInterval:(4-i)];
        NSLog(@"%zu %@", i, [NSThread currentThread]);
    });
    NSLog(@"done");
//运行结果:0 1 2 3 done
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//死锁情况1~~如果当前queue和执行queue相同会造成死锁
//死锁情况2~~在死锁1的情况下嵌套一个sync,同样会发生死锁,如下:
    dispatch_async(customSerialQueue, ^{
        dispatch_sync(mainQueue或者globalQueue或者customConcurrentQueue, ^{  //嵌套的sync
            dispatch_apply(4, customSerialQueue, ^(size_t i) {
                [NSThread sleepForTimeInterval:(4-i)];
                NSLog(@"%zu %@", i, [NSThread currentThread]);
            });
            NSLog(@"done");
        });
    });
  • 6.3 在globalQueue中执行
//block在多个线程中并发执行
    dispatch_apply(4, globalQueue, ^(size_t i) {
        [NSThread sleepForTimeInterval:(4-i)];
        NSLog(@"%zu %@", i, [NSThread currentThread]);
    });
    NSLog(@"done");
//运行结果:3 2 1 0 done
  • 6.4 在customConcurrentQueue中执行
//如果当前queue是customConcurrentQueue,则block都在当前线程中执行
//如果当前queue不是customConcurrentQueue,则block在多个线程中并发执行
    dispatch_apply(4, customConcurrentQueue, ^(size_t i) {
        [NSThread sleepForTimeInterval:(4-i)];
        NSLog(@"%zu %@", i, [NSThread currentThread]);
    });
    NSLog(@"done");
//运行结果:如果当前queue是customConcurrentQueue,则0 1 2 3 done
            如果当前queue不是customConcurrentQueue,则3 2 1 0 done
7. dispatch_once

整个程序运行中只会执行一次,彻底保证线程安全,用在单例模式上是坠吼的。

+ (instancetype)shareSingleTon{
    static SingleTon *shareSingleTonInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shareSingleTonInstance = [[SingleTon alloc] init];
    });
    return shareSingleTonInstance;
}
8. Dispatch barrier

queue需要为自建的customConcurrentQueue,其他情况都不适用(如果queue为globalQueue,则dispatch_barrier_async等价于dispatch_async,dispatch_barrier_sync等价于dispatch_sync)。

  • 8.1 dispatch_barrier_async
    不阻塞当前线程,确保customConcurrentQueue中之前插入的任务全部执行完自己再执行,确保自己执行完再执行之后插入的任务。
    dispatch_async(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:4];
        NSLog(@"1 %@", [NSThread currentThread]);  //新的线程
    });
    
    dispatch_barrier_async(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"2 %@", [NSThread currentThread]);  //async+customConcurrentQueue:新的线程
    });
    NSLog(@"3");
    
    dispatch_async(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"4 %@", [NSThread currentThread]);  //新的线程
    });
    NSLog(@"5");
//输出:3 5 1 2 4
    dispatch_async(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:4];
        NSLog(@"1 %@", [NSThread currentThread]);  //新的线程
    });
    
    dispatch_barrier_async(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"2 %@", [NSThread currentThread]);  //async+customConcurrentQueue:新的线程
    });
    NSLog(@"3");
    
    dispatch_sync(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"4 %@", [NSThread currentThread]);  //当前线程
    });
    NSLog(@"5");
//输出:3 1 2 4 5
  • 8.2 dispatch_barrier_sync
    阻塞当前线程,确保customConcurrentQueue中之前插入的任务全部执行完自己再执行,确保自己执行完再执行之后插入的任务。
    dispatch_async(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:4];
        NSLog(@"1 %@", [NSThread currentThread]);  //新的线程
    });
    
    dispatch_barrier_sync(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"2 %@", [NSThread currentThread]);  //sync+customConcurrentQueue:当前线程
    });
    NSLog(@"3");
    
    dispatch_async(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"4 %@", [NSThread currentThread]);  //新的线程
    });
    NSLog(@"5");
//输出:1 2 3 5 4
    dispatch_async(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:4];
        NSLog(@"1 %@", [NSThread currentThread]);  //新的线程
    });
    
    dispatch_barrier_sync(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"2 %@", [NSThread currentThread]);  //sync+customConcurrentQueue:当前线程
    });
    NSLog(@"3");
    
    dispatch_sync(customConcurrentQueue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"4 %@", [NSThread currentThread]);  //当前线程
    });
    NSLog(@"5");
//输出:1 2 3 4 5

9. dispatch_set_target_queue

GCD全解析
1
2

10. Dispatch source
11. Dispatch context
  • 11.1 dispatch_set_context
  • 11.2 dispatch_get_context
12. dispatch_async_f dispatch_sync_f
13. dispatch_semaphore

1

14. Reference

Thread Safety Summary
dispatch_sync死锁问题研究
GCD 深入理解:第一部分
小笨狼漫谈多线程:GCD(一)
Objective-C高级编程读书笔记之GCD
...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • #import "ViewController.h" @interface ViewController () @...
    艾克12138阅读 191评论 0 0
  • 简介 GCD(Grand Central Dispatch)是在macOS10.6提出来的,后来在iOS4.0被引...
    sunmumu1222阅读 803评论 0 2
  • 3.GCD GCD的全称是Grand Central Dispatch,提供了非常多的纯C语言的函数 GCD的优势...
    Mario_ZJ阅读 447评论 0 0
  • GCD为Grand Central Dispatch的缩写。[1] Grand Central Dispatch ...
    sea777777阅读 432评论 0 0
  • 常春藤教育 QQ群397757166 一、参加“常春藤教育”QQ群 每位学员参加课程管理员设立的QQ群,QQ群包括...
    Ivyleague阅读 582评论 0 1