名词介绍
任务
同步任务:按需直行前后需要等到
异步任务:可以并发执行,不需要等待
函数队列
先进先出, 新的任务总会插入到队列的最末尾,读取任务总会从队列的最前端读取
串行队列:任务按序一个一个执行
并发队列:可以多个任务同时并发执行,可通过开启线程来同时执行任务
GCD的使用
队列的创建方法 / 获取方法
创建队列 dispatch_queue_create,两个参数,第一个为队列唯一标识,类似于参数名的意思,第二个参数是串行队列还是并行队列:
DISPATCH_QUEUE_SERIAL
表示串行队列,DISPATCH_QUEUE_CONCURRENT
表示并发队列
所有的主队列任务都会放到主线程冲,可以用dispatch_get_main_queue()
方法获得主队列
可以使用 dispatch_get_global_queue
方法来获取全局并发队列。需要传入两个参数。第一个参数表示队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT
。第二个参数暂时没用,用 0
即可。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 异步执行 + 串行队列
dispatch_sync(queue, ^{ // 同步执行 + 当前串行队列
// 追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
});
});
串行队列不可以嵌套使用,会引起相互等待的问题,造成线程卡死
创建线程方法
[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
GCD其他用法
栅栏函数,拦截执行顺序 前后隔开
dispatch_barrier_async(queue, ^{
NSLog(@"barrier---%@",[NSThread currentThread])
});
延迟执行,设定时间后延迟执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2.0 秒后异步追加任务代码到主队列,并开始执行
NSLog(@"after---%@",[NSThread currentThread]); // 打印当前线程
});
一次性代码 一次执行 一般在单例模式时候使用
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行 1 次的代码(这里面默认是线程安全的)
});
快速迭代,可以异步快速执行任务,执行顺序串行队列依次执行,并行队列无须执行
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply---begin");
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
队列组
- 调用队列组的
dispatch_group_async
先把任务放到队列中,然后将队列放入队列组中。或者使用队列组的dispatch_group_enter
、dispatch_group_leave
组合来实现dispatch_group_async
。 - 调用队列组的
dispatch_group_notify
回到指定线程执行任务。或者使用dispatch_group_wait
回到当前线程继续向下执行(会阻塞当前线程)。
暂停线程
/**
* 队列组 dispatch_group_wait
*/
- (void)groupWait {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务 2
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
});
// 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group---end");
}
进入线程和离开线程 一般在不使用group时候 直接操作线程,作用和使用group类似
dispatch_group_enter
标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1dispatch_group_leave
标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1。- 当 group 中未执行完毕任务数为0的时候,才会使
dispatch_group_wait
解除阻塞,以及执行追加到dispatch_group_notify
中的任务。
/**
* 队列组 dispatch_group_enter、dispatch_group_leave
*/
- (void)groupEnterAndLeave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任务 2
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程.
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---end");
});
}
GCD 信号量:dispatch_semaphore
/**
* semaphore 线程同步
*/
- (void)semaphoreSync {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int number = 0;
dispatch_async(queue, ^{
// 追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
number = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,number = %zd",number);
}
从 Dispatch Semaphore 实现线程同步的代码可以看到:
semaphore---end
是在执行完number = 100;
之后才打印的。而且输出结果 number 为 100。这是因为异步执行
不会做任何等待,可以继续执行任务。
执行顺如下:- semaphore 初始创建时计数为 0。
异步执行
将任务 1
追加到队列之后,不做等待,接着执行dispatch_semaphore_wait
方法,semaphore 减 1,此时semaphore == -1
,当前线程进入等待状态。- 然后,异步任务 1 开始执行。任务 1 执行到
dispatch_semaphore_signal
之后,总信号量加 1,此时semaphore == 0
,正在被阻塞的线程(主线程)恢复继续执行。 - 最后打印
semaphore---end,number = 100
。
这样就实现了线程同步,将异步执行任务转换为同步执行任务。
线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作(更改变量),一般都需要考虑线程同步,否则的话就可能影响线程安全。