GCD

Queue

  • main queue: 主线程队列,串行队列。一般用于刷新UI。
  • global queue: 全局队列,并行队列。
  • custom queue: 自定义队列。
    *
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 一般用法
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i = 0; i < 100000; i++) {
    NSLog(@"%zd", i);
    }
    dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"刷新UI");
    });
    });

自定义队列

  • 串行队列

    • 同步运行

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      dispatch_queue_t serialQueue = dispatch_queue_create("kl.serialQueue", DISPATCH_QUEUE_SERIAL);
      for (int j = 0; j < 3; j++) {
      dispatch_sync(serialQueue, ^{
      for (int i = 0 ; i < 3; i++) {
      NSLog(@"current Thread %@ -- concurrentQueue %zd -- dispatch_async %zd",[NSThread currentThread] ,j, i);
      }
      });
      NSLog(@"run in mainQueue");
      }
      // 运行结果分析: 线程指针地址相同,是同一个线程;输出结果按序输出,串行队列先进先出。
      // `run in mainQueue` 出现在for循环之后即dispatch_sync任务执行完之后,因为串行队列同步运行,阻塞主线程。
      // 在这里发现创建的线程和主线程地址相同,说明串行队列同步运行是直接在主线程中运行的!
    • 异步运行

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      dispatch_queue_t serialQueue = dispatch_queue_create("kl.serialQueue", DISPATCH_QUEUE_SERIAL);
      for (int j = 0; j < 3; j++) {
      dispatch_async(serialQueue, ^{
      for (int i = 0 ; i < 3; i++) {
      NSLog(@"current Thread %@ -- concurrentQueue %zd -- dispatch_async %zd",[NSThread currentThread] ,j, i);
      }
      });
      NSLog(@"run in mainQueue");
      }
      // 运行结果分析: 线程指针地址相同,是同一个线程;输出结果按序输出,串行队列先进先出。
      // `run in mainQueue` 出现随机,因为是异步运行,不阻塞主线程。
  • 并行队列

    • 同步运行

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      dispatch_queue_t concurrentQueue = dispatch_queue_create("kl.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
      for (int j = 0; j < 3; j++) {
      dispatch_sync(concurrentQueue, ^{
      for (int i = 0 ; i < 3; i++) {
      NSLog(@"current Thread %@ -- concurrentQueue %zd -- dispatch_async %zd",[NSThread currentThread] ,j, i);
      }
      });

      NSLog(@"run in mainQueue current Thread %@", [NSThread currentThread]);
      }
      // 运行结果分析: 线程地址相同,且与主线程队列地址相同,
      // 结合串行队列同步运行与串行队列异步运行结果来看,同步运行时线程都是在主线程上运行,不开辟新的线程。
      // 运行结果与串行队列同步运行相同
    • 异步运行

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      dispatch_queue_t concurrentQueue = dispatch_queue_create("kl.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
      for (int j = 0; j < 3; j++) {
      dispatch_async(concurrentQueue, ^{
      for (int i = 0 ; i < 3; i++) {
      NSLog(@"current Thread %@ -- concurrentQueue %zd -- dispatch_async %zd",[NSThread currentThread] ,j, i);
      }
      });

      NSLog(@"run in mainQueue current Thread %@", [NSThread currentThread]);
      }
      // 运行结果分析: 输出结构乱序,因为是并行的异步执行,
      // 不能决定谁先谁后,且发现线程地址不同,说明开了多条线程执行队列,
      // `run in mainQueue` 出现随机,因为是异步运行,不阻塞主线程。

总结:

  1. dispatch_sync 并不会开辟新的线程执行任务,所以不管是串行队列还是并行队列其实都在一个线程(mainQueue也在主线程)中运行,且它是同步的,所以阻塞主线程,一定得队列任务完成之后才会执行之后的任务!
  2. dispatch_async 会异步的运行队列任务,但是串行队列只在一个线程中,所以只是不阻塞主线程,但是还是遵行串行队列FIFO(先进先出)执行任务, 而并行队列会开多条线程进行异步执行任务,效率更高!

dispatch_barrier

dispatch_barrier 之后的任务总是会在 dispatch_barrier 之前的任务执行完之后在执行

  • dispatch_barrier_sync

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    for (int i = 0; i < 3; i++) {
    dispatch_async(self.concurrentQueue, ^{
    NSLog(@"current Thread %@ -- dispatch_async_1 %zd",[NSThread currentThread], i);
    });
    }
    NSLog(@"dispatch_async_1_main");
    for (int i = 0; i < 3; i++) {
    dispatch_barrier_sync(self.concurrentQueue, ^{
    NSLog(@"current Thread %@ -- dispatch_barrier_sync %zd",[NSThread currentThread], i);
    if (i == 4) {
    NSLog(@"dispatch_barrier_sync finished");
    }
    });
    }
    NSLog(@"dispatch_barrier_sync_main");
    for (int i = 0; i < 3; i++) {
    dispatch_async(self.concurrentQueue, ^{
    NSLog(@"current Thread %@ -- dispatch_async_2 %zd",[NSThread currentThread], i);
    });
    }
    NSLog(@"dispatch_async_2_main");
    // 结果分析 先并发异步执行 dispatch_async_1,
    // 在执行 dispatch_barrier_sync ,最后并发异步执行 dispatch_async_2, dispatch_barrier_sync 会阻塞主线程
  • dispatch_barrier_async

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    for (int i = 0; i < 3; i++) {
    dispatch_async(self.concurrentQueue, ^{
    NSLog(@"current Thread %@ -- dispatch_async_1 %zd",[NSThread currentThread], i);
    });
    }
    NSLog(@"dispatch_async_1_main");
    for (int i = 0; i < 3; i++) {
    dispatch_barrier_async(self.concurrentQueue, ^{
    NSLog(@"current Thread %@ -- dispatch_barrier_async %zd",[NSThread currentThread], i);
    if (i == 2) {
    NSLog(@"dispatch_barrier_sync finished");
    }
    });
    }
    NSLog(@"dispatch_barrier_sync_main");
    for (int i = 0; i < 3; i++) {
    dispatch_async(self.concurrentQueue, ^{
    NSLog(@"current Thread %@ -- dispatch_async_2 %zd",[NSThread currentThread], i);
    });
    }
    NSLog(@"dispatch_async_2_main");
    // 结果分析: 首先它仍然会阻拦 dispatch_barrier_async 之后的任务等之前任务执行完之后再执行,
    // 其次他由于是异步的所以不阻塞主线程,
    // 但是我发现 `dispatch_barrier_async`里面执行的认为在一条线程中执行,且按顺序执行的!
    // 所以我们做耗时操作的时候尽量不要放在`dispatch_barrier_async`中执行,因为虽然他不阻塞主线程队列,但是会阻塞我们自创的队列啊!

注意:

  1. dispatch_barrier 不要用在global queue 中,因为 dispatch_barrier 只使用在一条并行队列中,而global queue 是每次系统分配一个并行队列(可能是不同的),所以没有意义!
  2. dispatch_barrier_async里面执行的认为在一条线程中执行,且按顺序执行的!所以我们做耗时操作的时候尽量不要放在dispatch_barrier_async中执行,因为虽然他不阻塞主线程队列,但是会阻塞我们自创的队列!
  3. NSDictionary: 线程安全, 但是NSMutableDictionary: 不是线程安全的,所以我们可以使用 dispatch_barrier_async 来保证 NSMutableDictionary 线程安全!(Get&&Set)

dispatch_semaphone: 信号量(用于并发控制)

  • dispatch_semaphore_create(3)
    创建信号量,传入一个大于等于0的long型整数(比作停车位,有了停车位才能停车)
  • dispatch_semaphore_signal(semaphone)
    1. 传入一个信号量,执行一次,增加一次semaphone计数(可以这么理解: 一辆车开走了,然后这个停车位就空出来了,算作增加一个停车位);
    2. 返回值为0时表示当前并没有线程等待其处理的信号量;
    3. 返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(优先级高的先被唤醒,否则随机)
  • dispatch_semaphore_wait(semaphone, dispatch_time(DISPATCH_TIME_NOW, 5));
    1. 每运行一次,semaphone计数-1,如果semaphone计数为0,那么根据传入的等待时间等待,如果等待时间设置为DISPATCH_TIME_FOREVER,那么就永远等待,永远不会执行之后的了, 除非信号量计数>1了!
    2. (可以这么理解: 在这里判断是否有停车位剩余,如果有就停车,没有的话就等待车位空出再停车,如果超出等待时间,这个人就等不下去了,开车走了);
    3. 如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// sample1:
dispatch_semaphore_t semaphone = dispatch_semaphore_create(2);
for (int i = 0; i < 10; i++) {
if(dispatch_semaphore_wait(semaphone, dispatch_time(DISPATCH_TIME_NOW, 2 *NSEC_PER_SEC)) == 0) {
dispatch_async(self.concurrentQueue, ^{
NSLog(@"i will sleep 3 second %zd", i);
sleep(3);
NSLog(@"i am wake up %zd", i);
NSLog(@"%zd signal %ld",i, dispatch_semaphore_signal(semaphone));
});
}else{
NSLog(@"等不下去了,走人");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
// sample2:
dispatch_semaphore_t semaphone = dispatch_semaphore_create(0);
NSLog(@"Boss: Kellen, wake up! working");
dispatch_async(self.concurrentQueue, ^{
NSLog(@"Kellen: I am sleep!");
sleep(5);
NSLog(@"Kellen: I am wake up!");
dispatch_semaphore_signal(semaphone);
});
dispatch_semaphore_wait(semaphone, DISPATCH_TIME_FOREVER);
NSLog(@"Kellen: I am working...");
// 等待执行 NSLog(@"Kellen: I am working...");

以上两种方案的话 看你情况使用!(信号量设为0 ,!0)

dispatch_group

  1. dispatch_group_notify
    监听dispatch_queue中所有的任务执行完成,执行某些操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, self.concurrentQueue, ^{
    for (int i = 0; i < 3; i++) {
    NSLog(@"1 %@ %zd", [NSThread currentThread], i);
    }
    });
    NSLog(@"haha 1111");
    dispatch_group_async(group, self.serialQueue, ^{
    for (int i = 0; i < 3; i++) {
    NSLog(@"2 %@ %zd", [NSThread currentThread], i);
    }
    });
    NSLog(@"haha 2222");
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 任务全部完成处理
    NSLog(@"组内任务全部完成,请检验...");
    });
  2. enter && levep

    • dispatch_group_enter(group); 进入组
    • dispatch_group_leave(group); 离开组
    • dispatch_group_wait(group, DISPATCH_TIME_FOREVER);(等待组内任务完成)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      dispatch_group_t group = dispatch_group_create();
      for (int i = 0; i < 3; i++) {
      dispatch_group_enter(group);
      dispatch_async(self.concurrentQueue, ^{
      NSLog(@"进入第%zd个异步 sleep 3秒", i);
      sleep(3);
      NSLog(@"离开第%zd个异步 sleep 完成", i);
      dispatch_group_leave(group);
      });
      }
      // 1 -----
      dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
      dispatch_async(dispatch_get_main_queue(), ^{
      NSLog(@"网络下载任务全部完成,请刷新UI");
      });
      // // 2 ------ 1/2 任选1
      // dispatch_group_notify(group, dispatch_get_main_queue(), ^{
      // NSLog(@"网络下载任务全部完成,请刷新UI");
      // });


-------------The End-------------

本文标题:GCD

文章作者:kysonyangs

发布时间:2016年06月10日 - 15:06

最后更新:2020年05月17日 - 16:05

原始链接:https://kysonyangs.github.io/default/GCD/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。