多线程(GCD)
GCD的基本使用
- GCD是苹果为多核并行运算提出的解决方案
- GCD会自动利用更多的内核
- GCD同NSOperation一样,也会自动管理线程的生命周期
- GCD是C的代码,较NSOperation性能更高
- GCD也是以队列的形式工作,FIFO
- 基本用法
//线程间通信
//进入子线程进行耗时操作
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
//回主线程进行刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
});
});
GCD的两种队列
- 并发队列(ConcurrentQueue):可以并发执行多个任务,允许开启多个线程同时执行任务(只是允许,要根据是否具有开启新线程的能力),所以只有异步函数才会对并发队列起作用,
- 串行队列(SerialQueue):让任务一个接着一个的执行,
- GCD自带一个特殊的串行队列为主队列,主队列自动会放到主线程中执行
- GCD还自带一个全局的并发队列,可以设置优先级(dispatch_get_global_queue)
同步和异步
- 同步:只能在当前线程中执行任务,不具备开启新线程的能力
- 异步:具备开启新线程的能力
|
同步 |
异步 |
串行 |
没有开启线程,串行执行任务 |
开启一条线程,串行执行任务 |
并发 |
没有开启线程,串行执行任务 |
开启线程(大于等于1条,不受控制),并发执行任务 |
各种函数队列演示代码
同步串行(打印结果12345)
//创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("duilie", DISPATCH_QUEUE_SERIAL);
//创建一个并行队列
dispatch_queue_t queeu = dispatch_queue_create("bbb", DISPATCH_QUEUE_CONCURRENT);
//同步执行这个串行队列(顺序执行,不会创建新的线程)
dispatch_sync(queue, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"4%@",[NSThread currentThread]);
});
NSLog(@"5%@",[NSThread currentThread]);
同步并行(打印结果12345)
//同步执行一个并行队列(顺序执行,没有开启线程,只是允许并行执行,但是没有线程这个条件,实际还是串行执行)
dispatch_sync(queeu, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
dispatch_sync(queeu, ^{
NSLog(@"2%@",[NSThread currentThread]);
});
dispatch_sync(queeu, ^{
NSLog(@"3%@",[NSThread currentThread]);
});
dispatch_sync(queeu, ^{
NSLog(@"4%@",[NSThread currentThread]);
});
NSLog(@"5%@",[NSThread currentThread]);
异步串行(打印结果1234,5不一定插在1234的哪个位置,因为1234和5是并行的,1234是串行的)
//异步执行这个串行队列(1234顺序执行,5和1234并行,开启一条线程,)
dispatch_async(queue, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4%@",[NSThread currentThread]);
});
NSLog(@"5%@",[NSThread currentThread]);
异步并行(打印结果随机,12345五个任务执行都是并发)
//异步执行一个并行队列(主线程和其他线程一起执行,顺序不一定,开启的线程数也不一定)
dispatch_async(queeu, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
dispatch_async(queeu, ^{
NSLog(@"2%@",[NSThread currentThread]);
});
dispatch_async(queeu, ^{
NSLog(@"3%@",[NSThread currentThread]);
});
dispatch_async(queeu, ^{
NSLog(@"4%@",[NSThread currentThread]);
});
NSLog(@"5%@",[NSThread currentThread]);
系统提供的五种队列(1主队列 + 4全局子队列)
//1.当前队列的优先级2.没用
/**
* DISPATCH_QUEUE_PRIORITY_HIGH 2
* DISPATCH_QUEUE_PRIORITY_DEFAULT 0
* DISPATCH_QUEUE_PRIORITY_LOW (-2)
* DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
*/
//获取到全局子队列
dispatch_queue_t qq = dispatch_get_global_queue(0, 0);
//获取主队列
dispatch_queue_t q = dispatch_get_main_queue();
GCD的其他函数
//延时执行
//参数:几秒后执行(填的是2,代表两秒后执行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延迟执行的代码片段");
});
NSLog(@"我先走了");
- 只执行一次(线程安全,只有创建单例用它,其他地方不要用)
//只执行一次(线程安全的)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只走一次");
});
//重复执行
//1.重复的次数 2.在什么队列重复 3.当前次数的索引
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t a) {
NSLog(@"%zu",a);
}) ;
- 线程组
- 本质是先把任务放到队列中,再把队列放入组内
- 优点:可以监听组内的任务完毕
//创建一个组(先把任务放进队列里,在放进组里,组可以监听任务完毕)
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"任务1");
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"任务2");
});
//监听组内执行情况(监听组内所有队列执行完毕)
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"执行完毕");
});
- 单独执行(dispatch_barrier_async)
- 本质是在并发队列中拥有串行的权利
- 如果有12345个任务,给3任务设置单独执行,按照12345的顺序添加任务,那么一定是12执行完才会执行3,3执行完才会执行45,所以12和45在执行的时候是并发,但是到了3,12和3和45其实是串行的
- 注意:如果想要这样设置其执行顺序(其实是仿照NSOperation设置依赖),那么所在的队列一定不能是系统提供的全局并发队列
//创建并发队列
dispatch_queue_t tt = dispatch_queue_create("tt", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(tt, ^{
NSLog(@"任务1");
});
dispatch_async(tt, ^{
NSLog(@"任务2");
});
//单独执行
dispatch_barrier_sync(tt, ^{
NSLog(@"任务3");
});
dispatch_async(tt, ^{
NSLog(@"任务4");
});
dispatch_async(tt, ^{
NSLog(@"任务5");
});
- block变成函数执行
- 本质是将block块变成c语言的函数,两种方式是一样的(dispatch_async和dispatch_async_f)
dispatch_async_f(dispatch_get_global_queue(0, 0), @"1", sum);
- void sum (void *a)
{
NSLog(@"%@",a);
}
资源抢夺
- 再实际开发过程中会将常出现卖票效应,其实就是多个线程访问同一个实例变量,并改变实例变量的值,就会导致实例变量的值发生错误,
- 互斥锁(@synchronized)有效的解决了这个问题
- 互斥锁原理:使用了线程同步技术,多条线程在同一条线上执行,并且按顺序执行
- 将要访问的实例变量套用在互斥锁里面
int a = 1000;
//资源抢夺(对象)
//互斥锁带的参数一定要传一个对象,一般情况传self
@synchronized(self) {
//锁的是代码
a = a - 1;
}