本文内容
任务、队列的概念、创建方式
任务 + 队列的6种组合的执行方式
线程间如何通信
dispatch_once、dispatch_after、dispatch_apply(快速迭代)、dispatch_barrier(栅栏函数)、dispatch_group(队列组)、dispatch_semaphore(信号量)如何实现线程安全与线程同步
iOS多线程demo地址
上文说到iOS 多线程- pThread和NSThread
这篇文章来讲讲GCD
GCD 的优点
- 可用于多核的并行运算
- 会自动利用更多的CPU内核
- 自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 只用关注执行什么任务,不用编写任何线程管理代码
1.任务
任务:执行的操作,放在block中的代码
执行任务的方式有两种,主要区别是:是否等待队列中的任务执行结束,是否具备开启新线程的能力
。
同步执行(sync):同步添加当前任务到指定的队列中,在队列中的任务全部结束之前,
会一直等待
,直到队列中的任务全部完成后,才开始下一个任务,只能在当前线程中执行任务,不具备
开启线程的能力。异步执行 (async):异步添加当前任务到指定队列中,
不会等待
队列的任务执行结束,直接开始执行下一个任务,可以在新的线程中执行任务,具备
开启线程的能力。
*注意:异步执行 (async)虽然具有开启线程的能力,但是不一定会开启新的线程,这跟任务所指定的队列有关
2.队列
队列:存放任务的队列,队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列末尾,而读取任务总是从队列的头部开始读取,每读取一个任务,队列中则释放一个任务。
队列有两种方式,都满足FIFO原则,主要区别是:执行顺序不同,开启线程数不同
- 串行队列(Serial Dispatch Queue ):只开启
一个
线程,一个
任务执行完毕后,再执行下一个任务 - 并行队列(Concurrent Dispatch Queue) :可以开启
多个
线程,并且同时
执行多个任务
3.使用步骤
- 创建一个队列
- 将任务添加到队列中,系统根据任务执行方式(同步、异步)进行执行
3.1 队列的创建、获取
使用dispatch_queue_create
创建队列
第一个参数:队列的唯一标识符,用于DEBUG,可以为空,推荐使用应用程序ID这种逆序全局域名。
第二个参数:队列类型,串行队列DISPATCH_QUEUE_SERIAL
,并行队列DISPATCH_QUEUE_CONCURRENT
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.queque", DISPATCH_QUEUE_SERIAL);
//创建并行队列
dispatch_queue_t queue2 = dispatch_queue_create("com.xiuxiu.queque", DISPATCH_QUEUE_CONCURRENT);
主队列Main Dispatch Queue
: GCD提供一种特殊串行队列:
- 所有放在主队列中的任务,都会放到主线程中执行
- 可使用
dispatch_get_main_queue()
获取主队列
主队列获取方法
dispatch_queue_t queue = dispatch_get_main_queue()
全局并发队列Global Dispatch Queue
: GCD默认提供的全局并发队列
并发队列获取方法:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_get_global_queue
获取全局队列
第一个参数:队列的优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
第二个参数:没有使用,用0 即可
3.2 任务的执行方式
dispatch_async(queue, ^{
//异步执行任务代码
});
dispatch_sync(queue, ^{
//同步执行任务代码
});
第一个参数是队列,那么队列 + 任务 执行方式就有6种组合(加上主队列)
同步执行 + 串行队列
异步执行 + 串行队列
同步执行 + 并行队列
异步执行 + 并行队列
同步执行 + 主队列
异步执行 + 主队列
3.2.1.同步执行 + 串行队列
/*
同步执行 + 串行队列
不会开启新线程,在当前线程中执行任务,一个任务执行完毕后,再执行下一个任务
*/
- (void)syncSerial{
NSLog(@" syncSerial start");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.syncSerial", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" syncSerial end");
}
:
前面的编号代表进程
的编号,一个APP的就是一个进程,进程编号总是一致的;
:
后面的编号代表线程
的编号, 21898代表线程的编号
输出结果:
- 在当前线程中执行任务,没有开启新线程(同步执行不具备开启线程能力),根据线程编号看出
- 所有任务都在
syncSerial start
和syncSerial end
之间执行(同步执行需要等待队列中的任务执行结束) - 任务按顺序执行(串行队列每次只有一个任务被执行,一个任务执行完毕后,再执行下一个任务)
2018-12-27 10:19:08.371664+0800 Thread[908:21898] syncSerial start
2018-12-27 10:19:08.371980+0800 Thread[908:21898] 任务一,i = 0
2018-12-27 10:19:09.372354+0800 Thread[908:21898] 任务一,i = 1
2018-12-27 10:19:10.372865+0800 Thread[908:21898] 任务一,i = 2
2018-12-27 10:19:11.373588+0800 Thread[908:21898] 任务二,i = 0
2018-12-27 10:19:12.374936+0800 Thread[908:21898] 任务二,i = 1
2018-12-27 10:19:13.375258+0800 Thread[908:21898] 任务二,i = 2
2018-12-27 10:19:14.376006+0800 Thread[908:21898] syncSerial end
3.2.2.异步执行 + 串行队列
/*
异步执行 + 串行队列
会开启新线程,但是因为队列是串行的,一个任务执行完毕后,再执行下一个任务
*/
- (void)asyncSerial{
NSLog(@" asyncSerial start");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.asyncSerial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" asyncSerial end");
}
输出结果:
- 开启一条线程(异步执行具备开启线程的能力,因为是串行队列,只开启了一条线程)
- 所有任务都在
asyncSerial start
和asyncSerial end
之后执行(异步执行不会等待,继续执行任务) - 任务按顺序执行(串行队列每次只有一个任务被执行,任务一个接着一个执行)
2018-12-27 10:43:17.947620+0800 Thread[1105:32316] asyncSerial start
2018-12-27 10:43:17.947916+0800 Thread[1105:32316] asyncSerial end
2018-12-27 10:43:17.948034+0800 Thread[1105:32360] 任务一,i = 0
2018-12-27 10:43:18.953286+0800 Thread[1105:32360] 任务一,i = 1
2018-12-27 10:43:19.956284+0800 Thread[1105:32360] 任务一,i = 2
2018-12-27 10:43:20.961804+0800 Thread[1105:32360] 任务二,i = 0
2018-12-27 10:43:21.965620+0800 Thread[1105:32360] 任务二,i = 1
2018-12-27 10:43:22.967181+0800 Thread[1105:32360] 任务二,i = 2
3.2.3. 同步执行 + 并行队列
/*
同步执行 + 并行队列
不会开启新线程,在当前线程中执行任务,一个任务执行完毕后,再执行下一个任务
*/
- (void)syncConcurrent{
NSLog(@" syncConcurrent start");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.syncConcurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" syncConcurrent end");
}
输出结果:
- 在当前线程中执行任务,没有开启新线程(同步执行不具备开启线程能力),根据线程编号看出,上面讲过
- 所有任务都在
syncConcurrent start
和syncConcurrent end
之间执行(同步执行需要等待队列中的任务执行结束) - 任务按顺序执行,虽然并行队列可以开启多个线程,并且同时执行多个任务,但是因为同步执行不具备开启线程的能力,只有当前这一个线程,而且同步执行需要等待队列中的任务执行结束,再执行下一个任务,所以任务只能一个接一个按照顺序执行。
2018-12-27 10:54:22.992819+0800 Thread[1210:37289] syncConcurrent start
2018-12-27 10:54:22.993023+0800 Thread[1210:37289] 任务一,i = 0
2018-12-27 10:54:23.994423+0800 Thread[1210:37289] 任务一,i = 1
2018-12-27 10:54:24.995012+0800 Thread[1210:37289] 任务一,i = 2
2018-12-27 10:54:25.996057+0800 Thread[1210:37289] 任务二,i = 0
2018-12-27 10:54:26.997429+0800 Thread[1210:37289] 任务二,i = 1
2018-12-27 10:54:27.998885+0800 Thread[1210:37289] 任务二,i = 2
2018-12-27 10:54:29.000327+0800 Thread[1210:37289] syncConcurrent end
3.2.4.异步执行 + 并发队列
/*
异步执行 + 并行队列
开启多个线程,任务交替执行
*/
- (void)asyncConcurrent{
NSLog(@" asyncConcurrent start");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" asyncConcurrent end");
}
输出结果:
- 开启了2个新线程(异步执行具备开启线程的能力)
- 所有任务都在
asyncConcurrent start
和asyncConcurrent end
之后执行(异步执行不会等待,继续执行任务) - 任务交替执行(异步执行具备开启线程的能力,并且并行队列可以开启多个线程,同时执行多个任务)
2018-12-27 11:18:23.174090+0800 Thread[1210:37289] asyncConcurrent start
2018-12-27 11:18:23.174253+0800 Thread[1210:37289] asyncConcurrent end
2018-12-27 11:18:23.174351+0800 Thread[1210:47038] 任务二,i = 0
2018-12-27 11:18:23.174401+0800 Thread[1210:37362] 任务一,i = 0
2018-12-27 11:18:24.177759+0800 Thread[1210:37362] 任务一,i = 1
2018-12-27 11:18:24.177759+0800 Thread[1210:47038] 任务二,i = 1
2018-12-27 11:18:25.178650+0800 Thread[1210:47038] 任务二,i = 2
2018-12-27 11:18:25.178650+0800 Thread[1210:37362] 任务一,i = 2
3.2.5.同步执行 + 主队列
同步执行 + 主队列 在不同线程中调用,结果不一样。
在主线程中调用,出现死锁
在其他线程中调用,不会开启线程,一个任务执行完毕后,再执行下一个任务
3.2.5.1在主线程调用
/*
同步执行 + 主队列
在主线程中调用,出现死锁
*/
- (void)syncMain{
NSLog(@" syncMain start");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" syncMain end");
}
输出结果:
在Xcode10 上运行崩溃,只输出了syncMain start
为什么?
因为我们在主线程中执行 syncMain
方法,相当于把 syncMain
任务放到主线程的队列中,而同步执行会等待当前队列中的任务执行完毕后,才会接着执行。我们把任务一追加到主队列中,任务一会等待主线程处理完syncMain
方法,而syncMain
方法又需要等待任务一执行完毕,才能继续执行,双方都在等待,所以线程死锁,任务无法执行。
2018-12-27 11:26:17.011738+0800 Thread[1443:50531] syncMain start
3.2.5.2 在其他线程调用
/*
同步执行 + 主队列
在其他线程中调用,不会开启线程,一个任务执行完毕后,再执行下一个任务
*/
- (void)syncMain{
NSLog(@"主线程");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@" syncMain start");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" syncMain end");
});
}
输出结果:
- 没有开启线程,放在主队列的任务都在主线程中执行
- 所有任务都在
syncMain start
和syncMain end
之间执行(同步执行需要等待队列中的任务执行结束) - 任务按顺序执行(串行队列每次只有一个任务被执行,任务一个接着一个执行)
2018-12-27 14:22:03.964047+0800 Thread[2134:84333] 主线程
2018-12-27 14:22:03.964238+0800 Thread[2134:84385] syncMain start
2018-12-27 14:22:03.965009+0800 Thread[2134:84333] 任务一,i = 0
2018-12-27 14:22:04.966453+0800 Thread[2134:84333] 任务一,i = 1
2018-12-27 14:22:05.967903+0800 Thread[2134:84333] 任务一,i = 2
2018-12-27 14:22:06.968817+0800 Thread[2134:84333] 任务二,i = 0
2018-12-27 14:22:07.969877+0800 Thread[2134:84333] 任务二,i = 1
2018-12-27 14:22:08.970376+0800 Thread[2134:84333] 任务二,i = 2
2018-12-27 14:22:09.971810+0800 Thread[2134:84385] syncMain end
3.2.6.异步执行 + 主队列
/*
异步执行 + 主队列
不会开启新线程,在主线程中执行,一个任务执行完毕后,再执行下一个任务
*/
- (void)asyncMain{
NSLog(@" asyncMain start");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@" asyncMain end");
}
输出结果:
- 没有开启线程,放在主队列的任务都在主线程中执行
- 所有任务都在
asyncMain start
和asyncMain end
之后执行(异步执行不会等待,继续执行任务) - 任务按顺序执行(因为主队列是串行队列,每次只有一个任务被执行,任务一个接着一个执行)
2018-12-27 14:33:15.293261+0800 Thread[2134:84333] asyncMain start
2018-12-27 14:33:15.293406+0800 Thread[2134:84333] asyncMain end
2018-12-27 14:33:15.293646+0800 Thread[2134:84333] 任务一,i = 0
2018-12-27 14:33:16.293875+0800 Thread[2134:84333] 任务一,i = 1
2018-12-27 14:33:17.295251+0800 Thread[2134:84333] 任务一,i = 2
2018-12-27 14:33:18.296751+0800 Thread[2134:84333] 任务二,i = 0
2018-12-27 14:33:19.297602+0800 Thread[2134:84333] 任务二,i = 1
2018-12-27 14:33:20.298579+0800 Thread[2134:84333] 任务二,i = 2
4.线程间通信
在iOS开发工程中,我们一般在主线程中进行UI刷新,如:点击、拖拽、滚动事件,耗时操作放在其他线程中,而当耗时操作结束后,回到主线程,就需要用到线程间的通信。
在全局队列中执行任务,任务完成后,切回主线程
- (void)gcdCommunication{
NSLog(@"我在主线程");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主线程");
});
});
}
输出结果:
2018-12-27 15:05:55.188427+0800 Thread[2519:102865] 我在主线程
2018-12-27 15:05:55.188635+0800 Thread[2519:102899] 任务一,i = 0
2018-12-27 15:05:56.189689+0800 Thread[2519:102899] 任务一,i = 1
2018-12-27 15:05:57.191253+0800 Thread[2519:102899] 任务一,i = 2
2018-12-27 15:05:58.195350+0800 Thread[2519:102865] 回到主线程
5.dispatch_once
创建单例或者整个程序只运行一次的代码,可以使用dispatch_once
,dispatch_once
函数保证这个程序运行过程中只被执行一次,即时在多线程情况下也是安全的。
/*
验证dispatch_once
*/
- (void)testGcdOnce{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (NSInteger i = 0; i < 3; i ++) {
[self gcdOnce];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (NSInteger i = 0; i < 3; i ++) {
[self gcdOnce];
}
});
}
- (void)gcdOnce{
NSLog(@"%s",__func__);
static TicketManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[TicketManager alloc]init];
NSLog(@"创建对象");
});
}
输出结果:dispatch_once
里面只被执行了一次
2018-12-27 15:19:31.228794+0800 Thread[2702:110251] -[ViewController gcdOnce]
2018-12-27 15:19:31.228794+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229290+0800 Thread[2702:110251] 创建对象
2018-12-27 15:19:31.229516+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229514+0800 Thread[2702:110251] -[ViewController gcdOnce]
2018-12-27 15:19:31.229630+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229678+0800 Thread[2702:110251] -[ViewController gcdOnce]
6.dispatch_after
dispatch_after
并不是在指定时间之后才执行处理,而是在指定时间之后将任务追加到队列中,这个指定时间并不是绝对准确的,想要大致完成延时任务可以使用dispatch_after
函数实现
- (void)gcdAfter{
NSLog(@"%s",__func__);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%s",__func__);
});;
}
输出结果:大致为1秒
2018-12-27 15:28:23.416958+0800 Thread[2781:114534] -[ViewController gcdAfter]
2018-12-27 15:28:24.513064+0800 Thread[2781:114534] -[ViewController gcdAfter]_block_invoke
7. dispatch_apply(快速迭代)
dispatch_apply
快速迭代方法,按照指定次数将指定的任务添加到队列中,并等待队列中的任务全部执行结束。
如果在串行队列中使用dispatch_apply
函数,就和for
循环遍历一样,按照顺序执行,体现不出快速迭代的意义。
如果在并行队列中使用dispatch_apply
函数,dispatch_apply
可以在多个线程中同时遍历多个数字。
7.1 在串行队列使用dispatch_apply
将在主队列dispatch_get_main_queue
的遍历任务放在并行队列dispatch_get_global_queue
中,为了避免上面讲的死锁问题,关注apply begin
和apply end
之间的代码即可
/*
dispatch_apply:快速迭代
*/
- (void)gcdApply{
NSLog(@"主线程");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"apply begin");
dispatch_apply(6, dispatch_get_main_queue(), ^(size_t index) {
NSLog(@"index = %zu",index);
});
NSLog(@"apply end");
});
}
输出结果:
- 没有开启线程(主队列的任务只在主线程中执行)
-
apply end
在最后输出(dispatch_apply
函数会等待队列全部任务执行结束)
2018-12-27 15:58:28.788666+0800 Thread[3090:128936] 主线程
2018-12-27 15:58:28.788880+0800 Thread[3090:128988] apply begin
2018-12-27 15:58:28.819630+0800 Thread[3090:128936] index = 0
2018-12-27 15:58:28.819791+0800 Thread[3090:128936] index = 1
2018-12-27 15:58:28.819897+0800 Thread[3090:128936] index = 2
2018-12-27 15:58:28.820107+0800 Thread[3090:128936] index = 3
2018-12-27 15:58:28.820396+0800 Thread[3090:128936] index = 4
2018-12-27 15:58:28.820503+0800 Thread[3090:128936] index = 5
2018-12-27 15:58:28.820752+0800 Thread[3090:128988] apply end
7.2在并行队列使用dispatch_apply
/*
dispatch_apply:快速迭代
*/
- (void)gcdApply{
NSLog(@"apply begin");
dispatch_apply(6, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"index = %zu",index);
});
NSLog(@"apply end");
}
输出结果:
- 开启线程(看线程编号得出)
-
apply end
在最后输出(dispatch_apply
函数会等待队列全部任务执行结束)
2018-12-27 15:39:06.219402+0800 Thread[2918:120358] apply begin
2018-12-27 15:39:06.219636+0800 Thread[2918:120391] index = 1
2018-12-27 15:39:06.219619+0800 Thread[2918:120358] index = 0
2018-12-27 15:39:06.219746+0800 Thread[2918:120391] index = 3
2018-12-27 15:39:06.219747+0800 Thread[2918:120392] index = 4
2018-12-27 15:39:06.219737+0800 Thread[2918:120358] index = 2
2018-12-27 15:39:06.219832+0800 Thread[2918:120391] index = 5
2018-12-27 15:39:06.219933+0800 Thread[2918:120358] apply end
需求1:我们需要异步执行两个操作(一个操作可以是一个任务,也可以是多个任务,这里是两个任务),而且第一组操作结束后,才能开始第二组操作,如何实现呢?
这里的意思其实保持线程同步,将异步任务转化为同步任务的意思。
方法1:使用栅栏函数
方法2:使用dispatch_group
方式3 :使用dispatch_semaphore
这三个方法都会在下面一一讲解的。
总结:就当前这个需求,使用栅栏函数会比较简单,所有方法讲完具体实现就可以看出来了不用创建group
或者semaphore
,直接放个栅栏在中间,分隔两个操作。
8.dispatch_barrier(栅栏函数)
栅栏函数存在的意义:先执行栅栏函数之前的任务,再执行栅栏函数中的任务,最后执行栅栏函数之后的任务,增加栅栏函数不影响原有队列的任务执行方式,也就是栅栏函数之前队列的任务是什么执行方式,栅栏函数之后队列的任务还是什么执行方式。
栅栏函数分为:dispatch_barrier_async
和dispatch_barrier_sync
区别就是 :添加栅栏函数后面任务到队列的时间不一样。
dispatch_barrier_sync
:需要
等待栅栏函数中的任务执行结束后,才会添加栅栏函数后的任务到队列中。
dispatch_barrier_async
: 不需要
等待栅栏函数中的任务执行结束,就已经将栅栏函数后的任务到队列中。
8.1 dispatch_barrier_sync
实现代码
- (void)gcdBarrier{
NSLog(@"主线程");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
//1.dispatch_barrier_sync
dispatch_barrier_sync(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" barrier1,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" barrier2,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@"---------barrier代码后面----------------");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
输出结果:
-
先
执行任务一、二,再
执行栅栏函数中的barrier1
、barrier2
,最后
执行任务三、四; - 任务一、二交替执行(异步执行),任务三、四交替执行(异步执行),印证栅栏函数不影响队列任务的执行方式;
- 横线在栅栏函数之后输出(
dispatch_barrier_sync
:需要等待栅栏函数中的任务执行结束后,才会添加栅栏函数后的任务到队列中)。
2018-12-28 17:07:54.227141+0800 Thread[11766:169507] 主线程
2018-12-28 17:07:54.227455+0800 Thread[11766:169563] 任务二,i = 0
2018-12-28 17:07:54.227482+0800 Thread[11766:169565] 任务一,i = 0
2018-12-28 17:07:55.231426+0800 Thread[11766:169563] 任务二,i = 1
2018-12-28 17:07:55.231426+0800 Thread[11766:169565] 任务一,i = 1
2018-12-28 17:07:56.232952+0800 Thread[11766:169563] 任务二,i = 2
2018-12-28 17:07:56.232985+0800 Thread[11766:169565] 任务一,i = 2
2018-12-28 17:07:57.236980+0800 Thread[11766:169507] barrier1,i = 0
2018-12-28 17:07:58.238423+0800 Thread[11766:169507] barrier1,i = 1
2018-12-28 17:07:59.239776+0800 Thread[11766:169507] barrier1,i = 2
2018-12-28 17:08:00.240315+0800 Thread[11766:169507] barrier2,i = 0
2018-12-28 17:08:01.240863+0800 Thread[11766:169507] barrier2,i = 1
2018-12-28 17:08:02.242310+0800 Thread[11766:169507] barrier2,i = 2
2018-12-28 17:08:03.242826+0800 Thread[11766:169507] ---------barrier代码后面----------------
2018-12-28 17:08:03.243200+0800 Thread[11766:169564] 任务三,i = 0
2018-12-28 17:08:03.243315+0800 Thread[11766:169663] 任务四,i = 0
2018-12-28 17:08:04.247765+0800 Thread[11766:169663] 任务四,i = 1
2018-12-28 17:08:04.247765+0800 Thread[11766:169564] 任务三,i = 1
2018-12-28 17:08:05.252405+0800 Thread[11766:169663] 任务四,i = 2
2018-12-28 17:08:05.252405+0800 Thread[11766:169564] 任务三,i = 2
8.2dispatch_barrier_async
实现代码
- (void)gcdBarrier{
NSLog(@"主线程");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
//2.dispatch_barrier_async
dispatch_barrier_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" barrier1,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" barrier2,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
NSLog(@"---------barrier代码后面----------------");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
输出结果:
-
先
执行任务一、二,再
执行栅栏函数中的barrier1
、barrier2
,最后
执行任务三、四; - 任务一、二交替执行(异步执行),任务三、四交替执行(异步执行),印证栅栏函数不影响队列任务的执行方式;
- 横线在栅栏函数之前输出(
dispatch_barrier_async
:不需要
等待栅栏函数中的任务执行结束,就已经将栅栏函数后的任务到队列中)。
2018-12-28 17:10:58.908322+0800 Thread[11798:171154] 主线程
2018-12-28 17:10:58.908586+0800 Thread[11798:171209] 任务一,i = 0
2018-12-28 17:10:58.908586+0800 Thread[11798:171154] ---------barrier代码后面----------------
2018-12-28 17:10:58.908616+0800 Thread[11798:171207] 任务二,i = 0
2018-12-28 17:10:59.912811+0800 Thread[11798:171207] 任务二,i = 1
2018-12-28 17:10:59.912811+0800 Thread[11798:171209] 任务一,i = 1
2018-12-28 17:11:00.917006+0800 Thread[11798:171207] 任务二,i = 2
2018-12-28 17:11:00.917047+0800 Thread[11798:171209] 任务一,i = 2
2018-12-28 17:11:01.919238+0800 Thread[11798:171209] barrier1,i = 0
2018-12-28 17:11:02.921574+0800 Thread[11798:171209] barrier1,i = 1
2018-12-28 17:11:03.924472+0800 Thread[11798:171209] barrier1,i = 2
2018-12-28 17:11:04.929280+0800 Thread[11798:171209] barrier2,i = 0
2018-12-28 17:11:05.930060+0800 Thread[11798:171209] barrier2,i = 1
2018-12-28 17:11:06.931831+0800 Thread[11798:171209] barrier2,i = 2
2018-12-28 17:11:07.934022+0800 Thread[11798:171209] 任务三,i = 0
2018-12-28 17:11:07.934022+0800 Thread[11798:171207] 任务四,i = 0
2018-12-28 17:11:08.939572+0800 Thread[11798:171207] 任务四,i = 1
2018-12-28 17:11:08.939573+0800 Thread[11798:171209] 任务三,i = 1
2018-12-28 17:11:09.942792+0800 Thread[11798:171209] 任务三,i = 2
2018-12-28 17:11:09.942829+0800 Thread[11798:171207] 任务四,i = 2
9.dispatch_group(队列组)
任务的执行是先创建一个任务,放入队列进行执行。而队列组就是用来存放队列的一个组。
将队列放入队列组中可以使用 dispatch_group_async
或者dispatch_group_enter
和dispatch_group_leave
的组合实现。
队列组的任务结束完成后,调用dispatch_group_notify
可以回到指定
线程执行任务,调用dispatch_group_wait
可以回到当前
线程执行任务(阻塞
当前线程)。
就上面的需求1,用dispatch_group
方式实现
1. 我们用dispatch_group_async
+ dispatch_group_notify
来进行实现
- (void)requirementGroupAsync{
NSLog(@"主线程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_group_notify(group, queue, ^{
NSLog(@"------dispatch_group_notify------");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
});
}
输出结果:先执行任务一、二,再执行任务三、四;
2018-12-28 17:59:23.695495+0800 Thread[12227:189820] 主线程
2018-12-28 17:59:23.695775+0800 Thread[12227:189867] 任务一,i = 0
2018-12-28 17:59:23.695778+0800 Thread[12227:189868] 任务二,i = 0
2018-12-28 17:59:24.697513+0800 Thread[12227:189867] 任务一,i = 1
2018-12-28 17:59:24.697514+0800 Thread[12227:189868] 任务二,i = 1
2018-12-28 17:59:25.701243+0800 Thread[12227:189868] 任务二,i = 2
2018-12-28 17:59:25.701243+0800 Thread[12227:189867] 任务一,i = 2
2018-12-28 17:59:26.705407+0800 Thread[12227:189868] ------dispatch_group_notify------
2018-12-28 17:59:26.705799+0800 Thread[12227:189868] 任务三,i = 0
2018-12-28 17:59:26.705803+0800 Thread[12227:189866] 任务四,i = 0
2018-12-28 17:59:27.709876+0800 Thread[12227:189868] 任务三,i = 1
2018-12-28 17:59:27.709873+0800 Thread[12227:189866] 任务四,i = 1
2018-12-28 17:59:28.713711+0800 Thread[12227:189866] 任务四,i = 2
2018-12-28 17:59:28.713711+0800 Thread[12227:189868] 任务三,i = 2
2. 我们用dispatch_group_async
+ dispatch_group_wait
来进行实现
- (void)requirementGroupAsync{
NSLog(@"主线程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
//2.dispatch_group_wait
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"-----dispatch_group_wait----");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
输出结果:
- 先执行任务一、二,再执行任务三、四;
-
dispatch_group_wait
在主线程输出,就如之前所说dispatch_group_wait
会阻塞当前线程。
2018-12-28 18:07:54.379565+0800 Thread[12300:193286] 主线程
2018-12-28 18:07:54.379782+0800 Thread[12300:193334] 任务二,i = 0
2018-12-28 18:07:54.379783+0800 Thread[12300:193332] 任务一,i = 0
2018-12-28 18:07:55.384688+0800 Thread[12300:193334] 任务二,i = 1
2018-12-28 18:07:55.384720+0800 Thread[12300:193332] 任务一,i = 1
2018-12-28 18:07:56.385807+0800 Thread[12300:193332] 任务一,i = 2
2018-12-28 18:07:56.385808+0800 Thread[12300:193334] 任务二,i = 2
2018-12-28 18:07:57.386847+0800 Thread[12300:193286] -----dispatch_group_wait----
2018-12-28 18:07:57.387161+0800 Thread[12300:193334] 任务三,i = 0
2018-12-28 18:07:57.387174+0800 Thread[12300:193332] 任务四,i = 0
2018-12-28 18:07:58.387806+0800 Thread[12300:193332] 任务四,i = 1
2018-12-28 18:07:58.387808+0800 Thread[12300:193334] 任务三,i = 1
2018-12-28 18:07:59.390173+0800 Thread[12300:193332] 任务四,i = 2
2018-12-28 18:07:59.390209+0800 Thread[12300:193334] 任务三,i = 2
3. 我们用dispatch_group_enter
+ dispatch_group_leave
+ dispatch_group_notify
来进行实现
- (void)requirementGroupEnter{
NSLog(@"主线程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_group_leave(group);
});
//1.dispatch_group_notify
dispatch_group_notify(group, queue, ^{
NSLog(@"------dispatch_group_notify------");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
});
}
输出结果:先执行任务一、二,再执行任务三、四;
2018-12-28 18:19:52.294127+0800 Thread[12422:198467] 主线程
2018-12-28 18:19:52.294429+0800 Thread[12422:198501] 任务一,i = 0
2018-12-28 18:19:52.294438+0800 Thread[12422:198504] 任务二,i = 0
2018-12-28 18:19:53.297632+0800 Thread[12422:198504] 任务二,i = 1
2018-12-28 18:19:53.297642+0800 Thread[12422:198501] 任务一,i = 1
2018-12-28 18:19:54.302891+0800 Thread[12422:198501] 任务一,i = 2
2018-12-28 18:19:54.302891+0800 Thread[12422:198504] 任务二,i = 2
2018-12-28 18:19:55.307933+0800 Thread[12422:198501] ------dispatch_group_notify------
2018-12-28 18:19:55.308242+0800 Thread[12422:198501] 任务三,i = 0
2018-12-28 18:19:55.308258+0800 Thread[12422:198502] 任务四,i = 0
2018-12-28 18:19:56.312331+0800 Thread[12422:198501] 任务三,i = 1
2018-12-28 18:19:56.312331+0800 Thread[12422:198502] 任务四,i = 1
2018-12-28 18:19:57.313584+0800 Thread[12422:198502] 任务四,i = 2
2018-12-28 18:19:57.313584+0800 Thread[12422:198501] 任务三,i = 2
4. 我们用dispatch_group_enter
+ dispatch_group_leave
+ dispatch_group_wait
来进行实现
- (void)requirementGroupEnter{
NSLog(@"主线程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务二,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
dispatch_group_leave(group);
});
// 2.dispatch_group_wait
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"-----dispatch_group_wait----");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
- 先执行任务一、二,再执行任务三、四;
-
dispatch_group_wait
在主线程输出,就如之前所说dispatch_group_wait
会阻塞当前线程
2018-12-28 18:21:29.197243+0800 Thread[12446:199547] 主线程
2018-12-28 18:21:29.197534+0800 Thread[12446:199590] 任务一,i = 0
2018-12-28 18:21:29.197598+0800 Thread[12446:199591] 任务二,i = 0
2018-12-28 18:21:30.200867+0800 Thread[12446:199591] 任务二,i = 1
2018-12-28 18:21:30.200870+0800 Thread[12446:199590] 任务一,i = 1
2018-12-28 18:21:31.204217+0800 Thread[12446:199590] 任务一,i = 2
2018-12-28 18:21:31.204215+0800 Thread[12446:199591] 任务二,i = 2
2018-12-28 18:21:32.205551+0800 Thread[12446:199547] -----dispatch_group_wait----
2018-12-28 18:21:32.205926+0800 Thread[12446:199590] 任务三,i = 0
2018-12-28 18:21:32.205930+0800 Thread[12446:199591] 任务四,i = 0
2018-12-28 18:21:33.210243+0800 Thread[12446:199590] 任务三,i = 1
2018-12-28 18:21:33.210413+0800 Thread[12446:199591] 任务四,i = 1
2018-12-28 18:21:34.215728+0800 Thread[12446:199590] 任务三,i = 2
2018-12-28 18:21:34.215728+0800 Thread[12446:199591] 任务四,i = 2
那dispatch_group_async
和dispatch_group_enter
+ dispatch_group_leave
有什么区别呢?
需求2:异步执行两个网络请求,两个网络请求执行结束后,进行一定的操作。
这里sendRequest
用来代表网络请求
- (void)sendRequest:(void (^)(void))block{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 1");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 1");
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block();
}
});
});
}
- (void)sendRequest2:(void (^)(void))block{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 2");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 2");
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block();
}
});
});
}
1. 用dispatch_group_async
实现
- (void)gcdGroupAsync{
NSLog(@"主线程");
// dispatch_group_async 里面,应该放同步代码,而不是异步代码
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[self sendRequest:^{
NSLog(@"sendRequest done");
}];
});
dispatch_group_async(group, queue, ^{
[self sendRequest2:^{
NSLog(@"sendRequest2 done");
}];
});
dispatch_group_notify(group, queue, ^{
NSLog(@"all task over");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主线程刷新UI");
});
});
}
输出结果:显示不符合需求,task1和task2还没有结束,就输出all task over 咯,为啥子呢?
原因:因为dispatch_group_async
里面放入的是异步的任务,dispatch_group_async
执行了sendRequest
这行代码后,就认为sendRequest
已经执行完毕了(其实还没有回调回来),group
不再持有这个任务,就会执行下面的dispatch_group_async
,而sendRequest2
同理,group
没有任务时,就会执行dispatch_group_notify
里面的任务,所以造成这样子的输出结果。
由此可见:dispatch_group_async
里面适合放入同步代码,而不是异步代码。
2018-12-29 17:13:42.710421+0800 Thread[17395:417245] 主线程
2018-12-29 17:13:42.710700+0800 Thread[17395:417334] start task 2
2018-12-29 17:13:42.710726+0800 Thread[17395:417333] all task over
2018-12-29 17:13:42.710708+0800 Thread[17395:417331] start task 1
2018-12-29 17:13:42.733662+0800 Thread[17395:417245] 回到主线程刷新UI
2018-12-29 17:13:45.714431+0800 Thread[17395:417331] end task 1
2018-12-29 17:13:45.714511+0800 Thread[17395:417334] end task 2
2018-12-29 17:13:45.714752+0800 Thread[17395:417245] sendRequest done
2018-12-29 17:13:45.714854+0800 Thread[17395:417245] sendRequest2 done
2.用dispatch_group_enter
+ dispatch_group_leave
实现
- (void)gcdGroupEnter{
NSLog(@"主线程");
dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self sendRequest:^{
NSLog(@"sendRequest done");
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self sendRequest2:^{
NSLog(@"sendRequest2 done");
dispatch_group_leave(group);
}];
dispatch_group_notify(group, queue, ^{
NSLog(@"all task over");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主线程刷新UI");
});
});
}
输出结果:当task1 和task2执行结束后,才输出all task over,符合我们的需求。
当我们调用sendRequest
时,先调用dispatch_group_enter
时,任务回调后再调用dispatch_group_leave
,整个异步操作中,任务是被group
持有的,只有回调结束后才离开group
,所以不会出现上面的问题。
注意:dispatch_group_enter
和dispatch_group_leave
成对存在
2018-12-29 17:32:12.739361+0800 Thread[17557:425760] 主线程
2018-12-29 17:32:12.739608+0800 Thread[17557:425809] start task 1
2018-12-29 17:32:12.739608+0800 Thread[17557:425810] start task 2
2018-12-29 17:32:15.742987+0800 Thread[17557:425809] end task 1
2018-12-29 17:32:15.742990+0800 Thread[17557:425810] end task 2
2018-12-29 17:32:15.743325+0800 Thread[17557:425760] sendRequest2 done
2018-12-29 17:32:15.743528+0800 Thread[17557:425760] sendRequest done
2018-12-29 17:32:15.743742+0800 Thread[17557:425810] all task over
2018-12-29 17:32:15.744000+0800 Thread[17557:425760] 回到主线程刷新UI
10.dispatch_semaphore(信号量)
dispatch_semaphore
使用计数来完成这个问题,计数为0 ,不可以通过,计数大于等于1,可以通过
其中有三个函数分别为:
-
dispatch_semaphore_create
:创建semaphore
并初始化信号量,初始化的值大于等于0; -
dispatch_semaphore_signal
:信号量+1; -
dispatch_semaphore_wait
: 判断当前信号量的值,如果当前信号量大于0,信号量-1,往下执行,如果当前信号量等于0,就会阻塞在当前线程,一直等待。
用处:
1、保持线程同步,将异步任务转化为同步任务
2、保证线程安全,为线程加锁
线程安全
:在多个线程中同时访问并操作同一对象时,运行结果与预期的值相同就是线程安全。
线程安全问题都是由全局变量及静态变量引起的,若每个线程中对全局变量
、静态变量
只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
线程同步
:可理解为线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。
10.1.保持线程同步,将异步任务转化为同步任务
这个也可以用来实现上面的需求1
- (void)gcdSemaphore{
NSLog(@"主线程");
dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务一,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
long x = dispatch_semaphore_signal(semaphore);
NSLog(@"signal后的信号量 = %ld",x);
});
long x = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"wait后的信号量 = %ld",x);
NSLog(@"---dispatch_semaphore_wait-----");
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务三,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i ++) {
NSLog(@" 任务四,i = %ld",(long)i);
[NSThread sleepForTimeInterval:1.0];
}
});
}
输出结果:
- 先输出任务一,再输出任务三、四
-
dispatch_semaphore_wait
在主线程输出(信号量为0 ,阻塞在当前线程)
我们在主线程创建了一个信号量赋值为0,并开辟了一个并行队列异步执行任务一,因为是一个异步操作,此时不会等待任务一执行结束, 直接执行到dispatch_semaphore_wait
,此时判断出信号量的值0,不可通行,阻塞当前线程,当运行到dispatch_semaphore_signal
时,信号量加1后等于1大于0,可通行,执行dispatch_semaphore_wait
后面的任务三、四,通行后信号量减1等于0。
2018-12-29 15:22:24.960944+0800 Thread[16315:368237] 主线程
2018-12-29 15:22:24.961180+0800 Thread[16315:368286] 任务一,i = 0
2018-12-29 15:22:25.965568+0800 Thread[16315:368286] 任务一,i = 1
2018-12-29 15:22:26.970160+0800 Thread[16315:368286] 任务一,i = 2
2018-12-29 15:22:27.975627+0800 Thread[16315:368237] wait后的信号量 = 0
2018-12-29 15:22:27.975627+0800 Thread[16315:368286] signal后的信号量 = 1
2018-12-29 15:22:27.975956+0800 Thread[16315:368237] ---dispatch_semaphore_wait-----
2018-12-29 15:22:27.976232+0800 Thread[16315:368287] 任务四,i = 0
2018-12-29 15:22:27.976285+0800 Thread[16315:368286] 任务三,i = 0
2018-12-29 15:22:28.978431+0800 Thread[16315:368286] 任务三,i = 1
2018-12-29 15:22:28.978445+0800 Thread[16315:368287] 任务四,i = 1
2018-12-29 15:22:29.980405+0800 Thread[16315:368286] 任务三,i = 2
2018-12-29 15:22:29.980405+0800 Thread[16315:368287] 任务四,i = 2
10.2.保证线程安全,为线程加锁
在上面讲NSThread的时候,讲过synchronized
和NSCondition
加锁方式,这里使用dispatch_semaphore
进行加密,运行结果和上面一致。
- (void)sale{
while (1) {
//1、synchronized
// @synchronized (self) {
// if (self.tickets > 0 ) {
// [NSThread sleepForTimeInterval:0.1];
// self.tickets --;
// self.saleCount = Total - self.tickets;
// NSLog(@"%@ , 卖出 = %d,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
// }else{
// break;//一定要break,不然就会死循环
// }
// }
// 2、NSCondition
// [self.condition lock];
// if (self.tickets > 0 ) {
// [NSThread sleepForTimeInterval:0.1];
// self.tickets --;
// self.saleCount = Total - self.tickets;
// NSLog(@"%@ , 卖出 = %d,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
// }else{
// break;
// }
// [self.condition unlock];
//
//3、dispatch_semaphore方式
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
if (self.tickets > 0 ) {
[NSThread sleepForTimeInterval:0.1];
self.tickets --;
self.saleCount = Total - self.tickets;
NSLog(@"%@ , 卖出 = %d,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
}else{
dispatch_semaphore_signal(self.semaphore);
break;
}
dispatch_semaphore_signal(self.semaphore);
}
}
信号量还需要多看点资料,这里就先这样子吧~~
上面就保持线程同步,将异步任务转化为同步任务,保证线程安全,给线程加锁就讲了好多种方式,选择的时候,针对需求而言来选择一个较好的方式就OK啦~
参考博客:
文章链接:
iOS 多线程- pThread和NSThread
iOS 多线程-NSOperation + NSOperationQueue
喜欢就点个赞吧✌️✌️
有错之处,还请指出,感谢🙏🙏