GCD全称Grand Central Dispatch,纯C语言,提供了很多强大的函数,现在ios开发中非常主流的多线程开发方式,记录一下基本原理以及面试中
GCD的两个核心概念:
任务:执行的操作 队列:用于存放任务
将任务添加到队列中,GCD会自动将任务取出,放到对应的线程中去执行,取出遵循队列的FIFO:先进先出
执行任务分为同步和异步: 主要影响能不能开启新的线程,针对的是线程
dispatch_sync 同步方式:只能在当前线程执行任务,不具备开启新线程的能力
dispatch_async 异步方式:可以在新的线程执行任务,具备开启新线程的能力
队列的类型分为串行和并行: 主要影响任务的执行方式,针对的是队列
DISPATCH_QUEUE_SERIAL 串行队列:任务按顺序的执行,一个任务执行完毕后再执行下一个任务
DISPATCH_QUEUE_CONCURRENT 并发队列:可以多个任务同时执行,自动开启多个线程执行任务,此队列只有在异步函数下才有效
//同步串行
dispatch_queue_t queue = dispatch_queue_create("com.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"任务");
});
//异步串行
dispatch_queue_t queue = dispatch_queue_create("com.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务");
});
//同步并发
dispatch_queue_t queue = dispatch_queue_create("com.gcd", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"任务");
});
//异步并发
dispatch_queue_t queue = dispatch_queue_create("com.gcd", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务");
});
这里有一个死锁的概念:使用sync函数往当前的串行队列中添加任务,会卡住当前的串行队列,造成死锁
死锁的两个必要条件:串行队列,同步任务
GCD的其他用法:
定时器:
NSTimer延时误差要大, NSTimer是在RunLoop中, RunLoop要处理各种东西(source,timer,observe),有时导致NSTimer不是特别准
GCD的定时器不受RunLoop中Mode的影响, 比如滚动TableView的时候,GCD的定时器不受影响,因为RunLoop内部也是基于GCD实现的
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"1");
});
dispatch_resume(timer);
其中dispatch_source_t timer;需要设置全局变量
延时操作:
GCD不可以取消延时
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1");
});
其他的实现方式:
perfromselect runloop要手动开启 主线程的runloop是默认开启的,而子线程需手动开启
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self performSelector:@selector(afterEvent:) withObject:self afterDelay:2];
NSLog(@"begin");
[[NSRunLoop currentRunLoop] run];
});
队列组:
可以让多个异步请求按照顺序去执行,当一个任务完成之后,一定要离开组,进入组和离开组必须成对出现
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t t = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);//进入组
dispatch_async(queue, ^{
//进行锁定的异步线程任务
dispatch_group_leave(group);//离开组
});
栅栏函数:
访问数据库或者文件的时候,读写锁,作用,可以更高效的访问数据库和文件,有效的避免数据竞争
dispatch_queue_t t = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(t, ^{
NSLog(@"1");
});
dispatch_async(t, ^{
NSLog(@"2");
});
dispatch_barrier_async(t, ^{
NSLog(@"barrier");
});
dispatch_async(t, ^{
sleep(2);
NSLog(@"3");
});
dispatch_async(t, ^{
NSLog(@"4");
});
sleep(2);
NSLog(@"end");
输出的结果能够保证barrier在12和34中间