iOS GCD线程同步
题外话
原本打算每周至少整理两篇博客的,看来真的是高估自己了...
在此给自己立个FLAG,一周至少一篇,恳请监督(大哭...)
言归正传
本篇博客主要讲解GCD的线程同步应用,对于什么是GCD的基础问题,不太明白的还是建议大家去看看基础,强烈推荐iOS多线程--彻底学会多线程之『GCD』(写的真的很棒哦)
线程同步
- 线程组group
- 信号量semaphore
- 栅栏barrier
一. 线程组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");
});
运行结果
分析
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");
}
运行结果
分析
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
...
方法
- dispatch_semaphore_create:创建一个信号量(semaphore)
- dispatch_semaphore_signal:信号通知,即让信号量+1
- dispatch_semaphore_wait:等待,直到信号量大于0时,即可操作,同时将信号量-1
还是上面的例子
/**
* 获取全局队列
* 参数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");
});
运行结果
分析
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_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");
});
运行结果
分析
根据打印结果可以看出
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只是阻塞同队列中后面的任务。
最后
若是本文内容有错误的地方,还请各位路过的大神们指点指点~