引言:为了提高我们的APP性能,充分利用苹果多核的系统,我们会经常用到多线程开发,多线程开发其实不是真正意义上的多个线程同时开发,而是多个线程之间进行了快速切换,从而达到多线程开发的目的,那我们常用的多线程有哪些呢?常用API又有哪些呢?我们常用的多线程有pthred(跨平台),NSThread(基于pthread封装),GCD(面向C),NSOperation(面向OC),对于我们IOS开发来说,最常用的是GCD和NSOperation,我们今天主要来了解下GCD的一些常见用法。
在介绍多线程之前,首先我们需要了解异步,同步,串行队列,并行队列这几个关键词
异步async:拥有开启线程的能力,不必立刻执行代码块
同步sync:没有开启新线程能力,立刻执行代码块
串行队列DISPATCH_QUEUE_SERIAL:遵循FIFO原则,执行完一个再执行下一个
并行队列DISPATCH_QUEUE_CONCURRENT:多线程执行,必须开启新线程才有效
根据同步异步及不同的队列,我们可以分成以下组合:
1.同步并发队列:没有开启新线程能力,串行执行,因为是同步,所以没有开启新线程的能力,单线程下只能串行执行
dispatch_queue_t queue = dispatch_queue_create("concurrent.test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
//需要执行的代码
});
2.同步串行队列,没有开启新线程的能力,串行执行,可能会造成死锁
dispatch_queue_t queue = dispatch_queue_create("serial.test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
});
3.同步主队列:没有开启新线程,串行执行,会造成死锁
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
});
4.异步并发队列:开启了新线程,并发执行任务,因为是异步,所以有开启新线程的能力,又是并发队列,所以是并发执行任务
dispatch_queue_t queue = dispatch_queue_create("coucurrent.test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
});
5.异步串行队列,开启新线程,串行执行任务,因为是异步,所以有开启新线程的能力,但是串行队列,所以依然是串行执行任务
dispatch_queue_t queue = dispatch_queue_create("serial.test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
});
6.异步主队列:没有开启新线程,串行执行任务,虽然是异步,但因为是主队列,所以只能在主线程下执行,不能开启新线程
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
});
我们通过以下的案例来分析一些组合情况,便于我们更清楚的了解多线程
案例一:
dispatch_queue_t queue = dispatch_queue_create("group1.test.queue", DISPATCH_QUEUE_SERIAL);//00
dispatch_async(queue, ^{//01
NSLog(@"1111------%@",[NSThread currentThread]);//02 dispatch_sync(queue, ^{//03 NSLog(@"2222------%@",[NSThread currentThread]);//04 });//05 NSLog(@"3333-------%@",[NSThread currentThread]);//06});//07
NSLog(@"4444-------%@",[NSThread currentThread]);//08
结果分析:以上案例会打印44444,11111,然后崩溃
执行00行,创建了一个串行队列
执行01,异步串行队列,开启新线程执行代码,因为是异步,所以不用立刻执行,跳过
执行08行,执行4444
方法执行完毕,回到02继续执行,打印1111
执行03行,同步串行队列,不开启新线程,在当前队列串行执行,因为是同步,所以要求立刻执行04代码块,但是同步队列是先进先出,执行完一个任务才能执行另一个,这个时候01-07的代码块还没执行完毕,没法立即执行04行代码,04行代码等待07行代码执行完毕,07行代码又在等待02-06行代码全部执行完毕才能释放,这个时候就造成互相等待,造成崩溃
案例二:
dispatch_queue_t queue = dispatch_queue_create("serial.group.test.queue", DISPATCH_QUEUE_SERIAL);//00
dispatch_queue_t queue2 = dispatch_queue_create("coucurrent.group.test.queue", DISPATCH_QUEUE_CONCURRENT);//01
dispatch_async(queue, ^{//02
NSLog(@"1111------%@",[NSThread currentThread]);//03 dispatch_sync(queue2, ^{//04 NSLog(@"2222------%@",[NSThread currentThread]);//05 });//06 NSLog(@"3333-------%@",[NSThread currentThread]);//07});//08
NSLog(@"4444-------%@",[NSThread currentThread]);//09
结果分析:这段代码会依次打印4444,1111,2222,3333
00行创建一个串行队列
01行创建一个并行队列
02行异步执行串行队列代码,因为是异步,所以可以不必立刻执行,跳过
执行09,打印4444
整个方法执行完毕回到03行继续执行,打印1111
执行04行,同步并行队列,不开启新线程,在当前队列同步执行,因为是同步所以立刻执行代码块
执行05,打印2222
执行06
执行07,打印3333
案例三:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1111");
[self performSelector: @selector(test) withObject:nil afterDelay:0.f];//往runloop添加了定时器
NSLog(@"3333");
});
-(void*)test
{
NSLog(@"222222");
}
结果分析:打印1111,3333,并没有执行test代码,
首先我们开启了一个全局线程异步队列,这时会开启一个子线程执行代码,
[self performSelector: @selector(test) withObject:nil afterDelay:0.f]这句代码本质是往runloop中添加了一个定时器,而我们知道子线程的runloop是不会默认开启的,所以不会执行test代码,我们要想执行test,可以往子线程的runloop添加port
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];