多线程GCD是基于task使用的,API都是C语言实现的。iOS6 之后由于dispatch对象已经支持ARC,所以在ARC工程下,不用再担心GCD生命周期问题了。
一. GCD队列
队列有串行队列和并行队列。串行队列一次只能执行一个任务,只有一个任务执行完成之后才能进行下一个任务; 并行队列可以同时执行多个任务, 系统会维护一个线程池来保证并行队列的执行。
1.1 队列的创建
// 串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("yourQueueId.com", DISPATCH_QUEUE_SERIAL);
// 并行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("yourQueueId.com", DISPATCH_QUEUE_CONCURRENT);
队列创建的函数dispatch_queue_create(), 使用时只需要传入队列的名字和属性即可。
label 参数: 队列的名字,便于识别
attr 参数: 队列的属性, 串行队列(DISPATCH_QUEUE_SERIAL)或并行队列(DISPATCH_QUEUE_CONCURRENT)
创建函数: dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr)
如果要申明一个dispatch queue, 可以使用strong
@property (nonatomic, strong) dispatch_queue_t oneQueue;
1.2 多线程的开启
GCD下多线程的开启有两种方法,且需要队列配合,开启多线程的方法有同步多线程和异步多线程。
// 创建串行队列
dispatch_queue_t q = dispatch_queue_create("yourQueueId.com", DISPATCH_QUEUE_SERIAL);
// 开启同步多线程
dispatch_sync(q, ^{
NSLog(@" %@----开启多线程", [NSThread currentThread]);
});
// 创建串行队列
dispatch_queue_t queue = dispatch_queue_create("myQueueId.com", DISPATCH_QUEUE_SERIAL);
// 开启异步多线程
dispatch_async(queue, ^{
NSLog(@" %@----开启多线程", [NSThread currentThread]);
});
这样就开启了一个多线程,系统就会帮我们管理生命周期,不需要我们去手动开启、关闭、销毁。好处就是我们不需要关心内存问题,坏处是自己不能进行线程的暂停等操作。
异步多线程在开启后,会新开辟一个线程去执行block中代码,当前线程依然继续往下执行。
同步多线程在开启后,不会新开辟一个线程,他会先执行block里面的代码,然后继续多线程后面的代码。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@" %@----当前线程", [NSThread currentThread]);
NSLog(@"---->1 ");
dispatch_queue_t q = dispatch_queue_create("yourQueueId.com", DISPATCH_QUEUE_SERIAL);
dispatch_sync(q, ^{
NSLog(@" %@----开启多线程", [NSThread currentThread]);
NSLog(@" --->2");
});
dispatch_queue_t queue = dispatch_queue_create("myQueueId.com", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@" %@----再开启一个多线程", [NSThread currentThread]);
NSLog(@" --->3");
});
NSLog(@" --->4");
}
看下我们的打印结果
可以看到开启同步线程时并没有新建一个,且打印顺序也是顺序的;开启异步线程时,新开辟了一个线程地址,且打印顺序也不是固定的(可能先4后3, 也可能是先3后4)。
我们可以这样理解同步线程和异步线程:
开始时只有我一个人在做某事,某个时刻领导又有了个新任务,如果我们使用同步线程,这时还是只有我自己做事,只是先让我做这个新任务,等我把新任务完成后再继续之前的事情;这时如果我们使用异步线程,就相对于我们向领导申请调了一个人过来帮忙做新任务,我自己还是继续做之前的事情。
我们也可以这样理解串行队列和并行队列:
串行队列:我自己手头上有多个任务时,只能按部就班按照任务被分配给我的时间,依次执行,当上个任务执行完后,才能执行新任务。
并行队列: 我手头上同时有多个任务,领导让我们同时做这几个任务,哪个任务会先完成,我们不能确定,但最终所有的任务都会被完成。
总结:
所以同步线程是一个人在做事,异步线程是多个人在做事;串行队列是一个人同时只能做一个事情,并行队列是一个人同时做多个事情。
二. GCD多线程的应用
2.1 多线程的延迟启动
在上面我们开启一个多线程是即时的,如果我们想定时开启怎么做?别担心,系统给我们提供了个延迟启动的APIdispatch_after。
// NSEC_PER_SEC 表示秒
// NSEC_PER_MSEC 表示毫秒
// NSEC_PRE_USEC 表示微秒
NSLog(@"开始");
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,(2 * NSEC_PER_SEC));
dispatch_after(time,dispatch_get_main_queue(),^{
NSLog(@"延迟启动");
});
2.2 三种创建队列的方法
在1.1节中我们学会了使用dispatch_queue_create创建串行和并行队列,加上系统给我们创建的2种独一无二的队列,我们一种就有3种创建队列的方法。
//系统创建的串行主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue() ;
// 系统创建的全局并行队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
// 自定义队列
dispatch_queue_t myQueue = dispatch_queue_create("myQueue.com", DISPATCH_QUEUE_SERIAL);
2.3 dispatch_barrier_async
我们知道串行队列是按照FIFO顺序执行的,所以可以预测哪个串行队列最后执行完
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
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]);
});
可以看到输出顺序是可以预测的。由于同步线程是按照顺序执行的,所以输出顺序也是可控的。
但是改为并行队列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
就会发现输出结果顺序就不确定了
如果我们想要等1和2先执行完后再去执行3和4, 我们知道GCD并没有提供给我们对多线程的生命周期的管理,怎么办了?查资料刚好有个API dispatch_barrier_async可以满足我们的要求,我们在1,2后面加上barrier_async函数,如下
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
可以运行下,看到结果正是我们所期望的,虽然1和2的顺序无法确定,但是是等1,2都执行完后才开始执行3,4的。
2.4 dispatch_group
在2.3节中我们知道dispatch_barrier_async可以控制某个线程执行完后再执行另外线程,那如果我们想知道某几个多线程什么时候执行完毕怎么操作,恰好系统也提供了一个API: dispatch_group
dispatch_queue_t dispatchQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"----4-----%@", [NSThread currentThread]);
});
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"end");
});
运行下,结果如下,可以看到通过 dispatch_group我们就能准确知道多个异步线程什么时候都执行完毕。