iOS点点滴滴

iOS GCD线程同步

2017-11-22  本文已影响15人  mmmickychiang7

题外话
原本打算每周至少整理两篇博客的,看来真的是高估自己了...
在此给自己立个FLAG,一周至少一篇,恳请监督(大哭...)

言归正传
本篇博客主要讲解GCD的线程同步应用,对于什么是GCD的基础问题,不太明白的还是建议大家去看看基础,强烈推荐iOS多线程--彻底学会多线程之『GCD』(写的真的很棒哦)

线程同步

一. 线程组group

可以使用dispatch_group_async函数将多个任务关联到一个线程组dispatch_group和相应的队列queue中,dispatch_group会并发地同时执行这些任务。
而且dispatch_group可以用来阻塞一个线程,直到dispatch_group关联的所有的任务完成执行。有时候必须等待任务完成的结果,然后才能继续后面的处理。

dispatch_group同步应用

首先执行任务1、任务2、任务3,执行完后,接着执行任务0

/**
* 创建一个队列
* 参数1:队列名(C语言写法)
* 参数2:队列类型(串行/并行)
* DISPATCH_QUEUE_SERIAL串行
* DISPATCH_QUEUE_CONCURRENT 并行
*/
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    
// 创建一个调度组group(线程组)
dispatch_group_t group = dispatch_group_create();
    
// 向线程组中添加多个任务
dispatch_group_async(group, queue, ^{
  NSLog(@"mission--1");
});
    
NSLog(@"===aaa===");
    
dispatch_group_async(group, queue, ^{
  NSLog(@"mission--2");
});
    
dispatch_group_notify(group, queue, ^{
  NSLog(@"dispatch_group_notify:mission--0");
});
    
dispatch_group_async(group, queue, ^{
  NSLog(@"mission--3");
});

运行结果

dispatch_group_01.png

分析
1.dispatch_group会等和它关联的所有的dispatch_queue上的任务都执行完毕才会发出同步信号,即dispathc_group_notify的代码块block才会被执行。
2.dispathc_group_notify的作用:在group中的其他操作全部完成后,再操作自己的内容,所以我们会看到上面任务1、任务2、任务3执行之后,才执行任务0。

问题升级

如使用AFNetworking添加异步任务时,上述方法显然无效。
因为网络请求需要时间,而线程的执行并不会等待请求完成后才真正算作完成,而是只负责将请求发出去,线程就认为自己的任务算完成了,当三个请求都发送出去,就会执行dispathc_group_notify中的内容,但请求结果返回的时间是不一定的,也就导致界面都刷新了,请求才返回,这就是无效的。
因此需要使用dispatch_group_enter、dispatch_group_leave(需要成对出现)

dispatch_group_t group = dispatch_group_create();
    
dispatch_group_enter(group);
[self demoBlock:^(NSString *str) {
  NSLog(@"mission--1");
  dispatch_group_leave(group);
}];
    
dispatch_group_enter(group);
[self demoBlock:^(NSString *str) {
  NSLog(@"mission--2");
  dispatch_group_leave(group);
}];
    
dispatch_group_enter(group);
[self demoBlock:^(NSString *str) {
  NSLog(@"mission--3");
  dispatch_group_leave(group);
}];
    
// 获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
dispatch_group_notify(group, mainQueue, ^{
  NSLog(@"mission--0");
});

为此我们还需要简单的写一个block回调
.m文件中添加扩展,写入:

- (void)demoBlock:(void(^)(NSString *str))block;

实现方法:

- (void)demoBlock:(void(^)(NSString *str))block {
    
    block(@"demoBlock");
}

运行结果

dispatch_group_02.png

分析
1.和dispatch_async相比,当我们调用n次dispatch_group_enter后再调用n次dispatch_group_leave时,dispatch_group_notify和dispatch_group_wait会收到同步信号(这里没有给出dispatch_group_wait的具体应用,需要了解的朋友可以自行查阅相关内容)
2.应用场景:处理异步任务的同步,当异步任务开始前调用dispatch_group_enter,异步任务结束后调用dispatch_group_leave。
适用于后台批量下载,结束后主线程统一刷新UI。

二. 信号量semaphore

首先,我们必须了解什么是信号量,在这里给大家简单的介绍一下(小白可以去查一些相关资料)


信号量.png

以下是简单的个人理解(伪代码)

 信号量
 信号量 初始值 v = 3
 
 a come
 if v < 0 wait
 else v = v - 1
 // v = 2; state: a in
 
 b come
 if v < 0 wait
 else v = v - 1
 // v = 1; state: b in
 
 c come
 if v < 0 wait
 else v = v - 1
 // v = 0; state: c in
 
 d come
 if v < 0 wait
 else v = v - 1
 // state: d wait
 
 e come
 if v < 0 wait
 else v = v - 1
 // state: e wait
 
 f come
 if v < 0 wait
 else v = v - 1
 // state: f wait
 
 b out // v = v + 1 = 1
 if v < 0 wait
 else v = v - 1
 // v = 0; state: f in
 
 ...

方法

还是上面的例子

/**
  * 获取全局队列
  * 参数1:选择的是哪个优先级的全局队列
  * 参数2:作为保留字段备用(一般为0)
*/
 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
 // 创建线程组
 dispatch_group_t group = dispatch_group_create();
    
 // 向线程组中添加任务
dispatch_group_async(group, globalQueue, ^{
  // 创建一个信号量 初始值为0
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  [self demoBlock:^(NSString *str) {
    NSLog(@"mission--1");
    // 执行了block之后 将信号量+1
    dispatch_semaphore_signal(semaphore);
  }];
  // 在成功执行block之前,信号量必须等待
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
    
NSLog(@"===aaa===");
    
dispatch_group_async(group, globalQueue, ^{
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  [self demoBlock:^(NSString *str) {
    NSLog(@"mission--2");
    dispatch_semaphore_signal(semaphore);
    }];
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
    
dispatch_group_async(group, globalQueue, ^{
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  [self demoBlock:^(NSString *str) {
    NSLog(@"mission--3");
    dispatch_semaphore_signal(semaphore);
  }];
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
    
dispatch_group_notify(group, globalQueue, ^{
  NSLog(@"dispatch_group_notify:mission--0");
});

运行结果

dispatch_semaphore.png

分析
1.在每个请求开始之前,我们创建一个信号量,初始为0,在请求操作之后,我们设一个dispatch_semaphore_wait,在请求到结果之后,再将信号量+1,即执行dispatch_semaphore_signal。
2.这样做的目的是保证在请求结果没有返回之前,一直让线程等待在那里,这样一个线程的任务一直在等待,就不会算作完成,notify的内容也就不会执行了,直到每个请求的结果都返回了,线程任务才能够结束,这时候notify也才能够执行。

三. 栅栏barrier

直接先上个例子:首先执行任务1、任务2、任务3,执行完后,接着执行任务0,任务0执行之后,再执行任务4、任务5、任务6


barrier.png

分别使用dispatch_barrier_sync函数和dispatch_barrier_async函数来完成

dispatch_barrier_sync

dispatch_queue_t queue = dispatch_queue_create("mickyQueue", DISPATCH_QUEUE_CONCURRENT);
    
dispatch_async(queue, ^{
  NSLog(@"mission--1");
});
    
dispatch_async(queue, ^{
  NSLog(@"mission--2");
});
    
dispatch_async(queue, ^{
  NSLog(@"mission--3");
});
    
dispatch_barrier_sync(queue, ^{
  for (int i = 0; i < 10000; i++) {
    if (700 == i) {
      NSLog(@"point1");
    } else if (800 == i) {
      NSLog(@"point2");
    } else if (900 == i) {
      NSLog(@"point3");
    }
  }
  NSLog(@"barrier");
});
    
NSLog(@"===aaa===");
    
dispatch_async(queue, ^{
  NSLog(@"mission--4");
});
    
NSLog(@"===bbb===");
    
dispatch_async(queue, ^{
  NSLog(@"mission--5");
});
    
dispatch_async(queue, ^{
  NSLog(@"mission--6");
});
    

运行结果

dispatch_barrier_sync.png

dispatch_barrier_async

dispatch_queue_t queue = dispatch_queue_create("mickyQueue", DISPATCH_QUEUE_CONCURRENT);
    
dispatch_async(queue, ^{
  NSLog(@"mission--1");
});
    
dispatch_async(queue, ^{
  NSLog(@"mission--2");
});
    
dispatch_async(queue, ^{
  NSLog(@"mission--3");
});
    
dispatch_barrier_async(queue, ^{
  for (int i = 0; i < 10000; i++) {
    if (700 == i) {
      NSLog(@"point1");
    } else if (800 == i) {
      NSLog(@"point2");
    } else if (900 == i) {
      NSLog(@"point3");
    }
  }
NSLog(@"barrier");
});
    
NSLog(@"===aaa===");
    
dispatch_async(queue, ^{
  NSLog(@"mission--4");
});
    
NSLog(@"===bbb===");
    
dispatch_async(queue, ^{
  NSLog(@"mission--5");
});
    
dispatch_async(queue, ^{
  NSLog(@"mission--6");
});

运行结果

dispatch_barrier_async_01.png dispatch_barrier_async_02.png

分析
根据打印结果可以看出
1.dispatch_barrier_sync/dispatch_barrier_async都是等待当前队列的之前插入的任务执行完之后再执行自己的任务,执行完自己的任务之后再执行当前队列的之后插入的任务。
2.aaa、bbb的输出位置完全不同
sync的时候,aaa、bbb的输出位置都是在任务0结束之后;
=async的时候,aaa、bbb的输出位置可以在任务0结束之前或者任务0正在执行中,又或者任务0结束之后。
3.barrier(dispatch_barrier_sync/dispatch_barrier_async)只是阻塞同队列中后面的操作而已,而dispatch_barrier_async不阻塞当前的线程。

结论
共同点
1.等待在它前面插入队列的任务先执行完;
2.等待他们自己的任务执行完再执行后面的任务。

不同点
1.dispatch_barrier_sync将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们;
2.dispatch_barrier_async将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务;
3.dispatch_barrier_async不阻塞当前的线程,而dispatch_barrier_sync/dispatch_barrier_async只是阻塞同队列中后面的任务。

最后

若是本文内容有错误的地方,还请各位路过的大神们指点指点~

上一篇下一篇

猜你喜欢

热点阅读