iOS多线程编程之GCD详解(一)

1. GCD简介

iOS开发中多线程的API主要有pthread,NSThread,NSOperation和GCD,前两者在现在开发过程中已经不常用,NSOperation是面向对象封装的一套API,而GCD则是一套纯C语言API。

引用下百度的介绍
GCD为Grand Central Dispatch的缩写。
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。

GCD有哪些优点:

  1. 提供了易于使用的并发模型而不仅仅只是锁和线程,可以让我们轻松的进行多线程编程。
  2. 自动利用更多的CPU内核。
  3. 自动管理线程的生命周期,以及多线程调度。
  4. 提供了简单创建单例的接口等等。

2. Dispatch_Queue(队列)概念

队列:

GCD中的队列和数据结构中的队列特性上一致,都是受限制的线性表,遵循FIFO(First In First Out),即新的任务需要在队尾加入,读取任务则从队首部读取,即先进先出
队列又分为:

  • 串行队列(Serial Dispatch Queue)
    简单理解串行队列就是按顺序执行一个任务,上一个任务执行完毕后执行下一个任务,现实中的例子,去银行办业务,只有一个窗口,大家有秩序的排队办业务,即是串行队列

  • 并发队列(Concurrent Dispatch Queue)
    简单理解并发队列就是同时执行多个任务,现实中的极端例子,去银行排队办业务,有多个窗口同时可以办业务,即是并发队列,需要注意的是,这些任务会按照被添加的顺序依次开始执行。但是任务完成的顺序是任意的。

这里提到了“任务”的概念:
“任务”,在 GCD 里指的是 Block,即一段需要执行的代码块
任务执行方式有两种:

  • 同步执行(dispatch_sync)
    完成任务后才会返回,进行下一任务,可见同步不具备开启线程能力,只会在当前线程依次执行

  • 异步执行(dispatch_async)
    完成任务后立即返回,进行下一任务,具备多线程能力

总结:并发队列只会在异步执行下生效,同步执行不会触发多线程创建。

3. GCD队列编程实现

  1. 创建一个队列
//串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    
    //并发队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_queue_create创建一个dispatch_queue_t队列,第一个参数设置该队列标识符,用于调试使用,第二个参数,则是队列类型
DISPATCH_QUEUE_SERIAL串行队列
DISPATCH_QUEUE_CONCURRENT 并发队列

除了自己创建队列之外,系统提供了两个队列供我们获取使用

    //主线程队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //全局队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
  • 主线程队列:这个是主线程的串行队列
  • 全局队列: 这是个全局的并发队列,很多时候可以不需要自己创建并发队列,直接获取全局队列即可 第一个参数为优先级,这是个大概优先级设置
DISPATCH_QUEUE_PRIORITY_HIGH  //高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT //默认优先级
DISPATCH_QUEUE_PRIORITY_LOW //低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND //后台优先级 

接下来即可在队列中添加任务执行

//异步执行
    dispatch_async(serialQueue, ^{
        NSLog(@"serialQueue中异步执行");
    });
    
    //同步执行
    dispatch_sync(serialQueue, ^{
        NSLog(@"serialQueue中同步执行");
    });

这里总共有三种类型队列:

  1. 并发队列(包括全局队列)
  2. 串行队列
  3. 主线程队列

三种队列分别可以同步异步执行就有6种组合方式

  • 主线程同步执行
  • 主线程异步执行
  • 串行队列同步执行
  • 串行队列异步执行
  • 并行队列同步执行
  • 并行队列异步执行

接下来我们依次看下每种情况

3.1 主线程同步执行

    NSLog(@"Begin");
    
    //主线程同步同步执行
    dispatch_sync(mainQueue, ^{
        NSLog(@"主线程同步同步执行");
    });
    
    NSLog(@"End");

运行下我们会发现

2017-05-03 16:50:31.542 GCDDemo[41489:596607] Begin
只打印了begin,并没有执行下去
事实上这里发生了死锁,
之前说过dispatch_sync会等待上一个任务执行完才会执行下一个任务,sync添加的任务需要执行,需要等待NSLog(@"End");执行完毕,```NSLog(@"End");``任务本身也添加在主线程队列中,所以执行这个任务的前提是sync添加的任务执行完毕,这就出现了死锁,两个任务互相等待

总结:
1. 主线程中执行同步任务会发生死锁
2. 串行队列中嵌套串行队列任务会发生死锁(这个留给大家自己验证,本质上和主线程同步发生死锁一致)

3.2 主线程异步执行

    NSLog(@"Begin");
    //主线程异步执行
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------主线程异步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------主线程异步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------主线程异步执行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

运行结果

2017-05-03 17:09:26.031 GCDDemo[42671:613068] Begin
2017-05-03 17:09:26.031 GCDDemo[42671:613068] End
2017-05-03 17:09:26.042 GCDDemo[42671:613068] 0---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}

2017-05-03 17:09:26.043 GCDDemo[42671:613068] 1---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.044 GCDDemo[42671:613068] 2---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.052 GCDDemo[42671:613068] 0---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.057 GCDDemo[42671:613068] 1---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.059 GCDDemo[42671:613068] 2---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.060 GCDDemo[42671:613068] 0---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.062 GCDDemo[42671:613068] 1---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.063 GCDDemo[42671:613068] 2---------主线程异步执行<NSThread: 0x60000007d5c0>{number = 1, name = main}

总结:
主线程队列异步执行不会开辟线程,会在当前线程同步执行。

3.3 串行队列同步执行

   NSLog(@"Begin");
    //串行队列同步执行
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------串行队列同步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------串行队列同步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------串行队列同步执行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

运行结果如下:

2017-05-03 17:25:48.225 GCDDemo[43694:625941] Begin
2017-05-03 17:25:48.225 GCDDemo[43694:625941] 0---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 1---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 2---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 0---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 1---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 2---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 0---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] 1---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] 2---------串行队列同步执行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] End

总结: 串行队列异步执行不会开辟多线程,只会在一条线程中依次执行

3.4 串行队列异步执行

    NSLog(@"Begin");
    //串行队列异步执行
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------串行队列异步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------串行队列异步执行%@", i,[NSThread currentThread]);
        }
    });
 dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------串行队列异步执行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

运行结果如下

2017-05-03 17:20:16.514 GCDDemo[43349:621375] Begin
2017-05-03 17:20:16.514 GCDDemo[43349:621375] End
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 0---------串行队列异步执行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 1---------串行队列同步执行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 2---------串行队列异步执行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 0---------串行队列异步执行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 1---------串行队列异步执行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 2---------串行队列异步执行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 0---------串行队列异步执行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.517 GCDDemo[43349:621680] 1---------串行队列异步执行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.517 GCDDemo[43349:621680] 2---------串行队列异步执行<NSThread: 0x608000268040>{number = 3, name = (null)}

总结: 串行队列异步执行不会开辟多线程,只会在一条线程中依次执行

细心的同学会发现串行队列同步和异步执行都没有开辟多线程,在一条线程中同步执行,那么对于串行队列同步和异步执行有什么区别呢?
区别只有一点:

dispatch_async:不会阻塞当前队列,立即返回添加当前队列后面任务,可以看到上图打印结果,先打印end。再打印async任务

dispatch_sync:会阻塞当前队列,等该sync任务全部执行完毕之后再添加当前队列后面任务,可以看到上图打印结果,先打印完sync任务打印end。

3.5 并发队列同步执行

    NSLog(@"Begin");
    //串行队列同步执行
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------并发队列同步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------并发队列同步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------并发队列同步执行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

运行结果如下:

2017-05-03 17:34:24.407 GCDDemo[44222:633000] Begin
2017-05-03 17:34:24.407 GCDDemo[44222:633000] 0---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.407 GCDDemo[44222:633000] 1---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 2---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 0---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 1---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 2---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 0---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 1---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.409 GCDDemo[44222:633000] 2---------并发队列同步执行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.410 GCDDemo[44222:633000] End

总结: 并发队列同步执行不会开辟多线程,只会在一条线程中依次执行

3.6 并发队列异步执行

    NSLog(@"Begin");
    //并发队列异步执行
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------并发队列异步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------并发队列异步执行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------并发队列异步执行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

运行结果如下

2017-05-03 17:38:10.893 GCDDemo[44461:636216] Begin
2017-05-03 17:38:10.894 GCDDemo[44461:636216] End
2017-05-03 17:38:10.894 GCDDemo[44461:636252] 0---------并发队列异步执行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.894 GCDDemo[44461:636254] 0---------并发队列异步执行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.894 GCDDemo[44461:636251] 0---------并发队列异步执行<NSThread: 0x60800007a800>{number = 3, name = (null)}
2017-05-03 17:38:10.897 GCDDemo[44461:636252] 1---------并发队列异步执行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.899 GCDDemo[44461:636254] 1---------并发队列异步执行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.900 GCDDemo[44461:636251] 1---------并发队列异步执行<NSThread: 0x60800007a800>{number = 3, name = (null)}
2017-05-03 17:38:10.903 GCDDemo[44461:636252] 2---------并发队列异步执行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.904 GCDDemo[44461:636254] 2---------并发队列异步执行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.905 GCDDemo[44461:636251] 2---------并发队列异步执行<NSThread: 0x60800007a800>{number = 3, name = (null)}

总结: 并发队列异步执行会开辟多线程执行,并且执行顺序不定

各种组合的结果总结如下表

同步执行 异步执行
主线程队列 出现死锁 不会开辟多线程,在主线程上串行执行
串行队列 不会开辟多线程,在一条线程上串行执行 不会开辟多线程,在一条线程上串行执行
并发队列(全局队列) 不会开辟多线程,在一条线程上串行执行 开辟多线程执行,并且执行顺序不定

划重点:
1. 并发队列只会在异步执行下才会开启多线程执行
2. 在主线程队列同步执行,或者串行队列嵌套串行队列同步任务会发生死锁
3. dispatch_async: 不会阻塞当前队列,立即返回添加当前线程后面任务。
4. dispatch_sync:会阻塞当前队列,等该sync任务全部执行完毕之后再添加当前队列后面任务

4.应用示例

  1. 异步处理数据完成后,主线程更新UI界面
dispatch_async(globalQueue, ^{
        //异步数据处理...
        dispatch_async(mainQueue, ^{
            //主线程更新UI
            
        });
    });

可以看到运用GCD可以轻松的进行线程间通信

  1. 使用指定队列进行串行处理任务,例如数据库存储等依赖线程安全等处理
//串行队列保证线程安全
    dispatch_sync(serialQueue, ^{
        //数据存储等依赖线程安全操作...
    });

FMDBDataBaseQueue就是使用的串行队列来保证线程安全的

5. 其他GCD API

5.1 dispatch_after

dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"After");
    });

延迟执行,有时候可以作为定时执行作用,需要注意的是,该函数并不是在指定时间后执行处理,而只是在指定时间追加处理到dispatch_queue,实际执行时间受到runloop的状态影响,存在偏差。

5.2 dispatch_once

static dispatch_once_t onceToken;  
    dispatch_once(&onceToken, ^{  
        sharedManager = [[SchoolManager alloc] init];  
    });  

执行一次,在block中的代码全局只会执行一次,被广泛用于单例创建中

5.3 dispatch_suspend / dispatch_resume

//挂起队列 
dispatch_suspend(queque);
//恢复队列
 dispatch_resume(queue);

挂起对已经执行的任务没有影响,会暂停所有未执行的任务以及后续追加的任务
恢复则会继续执行所有被挂起的任务

5.4 dispatch_set_target_queue

//搬运一段代码

dispatch_queue_t mySerialDispatchQueue =
    dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground =
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);

dispatch_set_target_queue主要有两个作用

  1. 设置优先级,自建的队列优先级默认和系统队列优先级一致,设置参数1队列的优先级和参数2的优先级一致,显然你不能设置系统全局队列和主队列优先级
  2. 更改队列的执行层级,如果多个串行队列设置函数目标串行队列是某一个串行队列,原本并发执行的串行队列,在目标串行队列上只能依次执行,代码示例如下
    dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    dispatch_set_target_queue(queue3, targetQueue);
    dispatch_async(queue1, ^{
        NSLog(@"1 in");
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"1 out");
    });
    dispatch_async(queue2, ^{
        NSLog(@"2 in");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"2 out");
    });
    dispatch_async(queue3, ^{
        NSLog(@"3 in");
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"3 out");
    });

5.5 dispatch_group

主要应对这样的需求,异步处理完A和B任务,两者都执行完执行C任务,和NSOperation中的依赖一致。示例如下

 dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //异步耗时操作A
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //异步耗时操作B
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 前面的异步操作A和B都执行完之后,回主线程
    });

5.6 dispatch_apply

这是dispatch_syncdispatch_group的关联API,按指定次数将指定的Block追加到指定的Dispatch_Queue中,并且等待全部执行结束。可以用于遍历效果

//全局队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(10, globalQueue, ^(size_t index) {
        //重复执行10次
        NSLog(@"%zu",index);
    });
    
    //10次执行完之后,再执行Done
    NSLog(@"Done");

5.7 dispatch_barrier_sync / dispatch_barrier_async

栅栏:有时候创建两组并发任务,如果在中间加入栅栏,那么这个任务会在第一组任务完成后执行,并且第二组任务会在栅栏任务完成后才开始执行,如下图所示在并发队列中添加任务,执行顺序一定是
任务组A->Barrier任务->任务组B

dispatch_barrier.png

示例代码如下:

dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^{
        NSLog(@"1---------");
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"2--------");
    });

    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"barrier--------");
    });

    dispatch_async(concurrentQueue, ^{
        NSLog(@"3--------");
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"4--------");
    });

打印执行顺序1,2不定,3,4也不定,但是barrier一定在1和2之后,3和4一定在barrier之后,可以自行添加数量测试。

应用场景,经常我们会自行创建一个队列进行文件读取和存储,一般文件读取的速度很快,可以使用并发队列多线程提高读取效率,但是文件存储需要考虑到线程安全,那么我们就可以使用barrier进行文件存储操作,类似这样

dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^{
        //文件读取
    });
    dispatch_async(concurrentQueue, ^{
        //文件读取
    });
    
    dispatch_barrier_async(concurrentQueue, ^{
        //文件存储
    });
    
    dispatch_barrier_async(concurrentQueue, ^{
        //文件存储
    });
    dispatch_async(concurrentQueue, ^{
        //文件读取
    });

可见使用barrier可以轻松高效的实现文件IO。

dispatch_barrier需要注意的点

  1. dispatch_barrier只会对自建的队列生效,对于系统的mainQueue和GlobalQueue不起作用
  2. dispatch_barrier_asyncdispatch_barrier_sync的区别也同样在于同步和异步,dispatch_barrier_async不会等待自己任务执行完毕才会在队列中添加其他任务,而dispatch_barrier_sync会等待自己任务执行完毕后才会在队列中添加其他任务。

AFNetworking中大量使用dispatch_barrier_async做数据存储,可以看到dispatch_barrier_async也可以实现串行同步队列效果,相比于dispatch_sync容易产生死锁(在串行队列中同步添加该串行队列任务即会发生死锁),dispatch_barrier_async更加安全。

全文总结:
以上介绍了GCD中绝大多数常用API,可以看到GCD的灵活性,通过在应用中合理使用GCD来提高程序的执行效率。

参考书籍:
Objective-C高级编程

后篇:
iOS多线程编程之GCD详解(二)完结

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

推荐阅读更多精彩内容

  • 一、前言 上一篇文章iOS多线程浅汇-原理篇中整理了一些有关多线程的基本概念。本篇博文介绍的是iOS中常用的几个多...
    nuclear阅读 2,043评论 6 18
  • 一、前言 本篇博文介绍的是iOS中常用的几个多线程技术: NSThread GCD NSOperation 由于a...
    和珏猫阅读 571评论 0 1
  • 1. GCD简介 什么是GCD呢?我们先来看看百度百科的解释简单了解下概念 引自百度百科:Grand Centra...
    千寻_544f阅读 359评论 0 0
  • 在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案...
    张战威ican阅读 600评论 0 0
  • “死亡需要勇气,但痛苦地活着才是真正的勇士。” 一开始注意到《万箭穿心》这部作品,是因为演员颜丙燕号称可...
    还牵着你的手阅读 795评论 0 0