正文
在iOS中,实现多线程的方式有很多,比如GCD,NSOperation,NSThread等等,但是一直对线程的概念模糊,今天就根据代码例子来了解iOS中GCD的用法和原理。
GCD是和block紧密相连的,所以最好先了解下block。GCD是C level的函数,这意味着它也提供了C的函数指针作为参数,方便了C程序员。
下面首先来看GCD的使用:dispatch_async(dispatch_queue_t queue, dispatch_block_t block);async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了。(除了async,还有sync,delay,本文以async为例)。
dispatch队列不支持cancel(取消),没有实现dispatch_cancel()函数,不像NSOperationQueue,不得不说这是个小小的缺憾。
之所以程序中会用到多线程是因为程序往往会需要读取数据,然后更新UI。为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程。GCD里就有三种queue来处理。
GCD Queue分为三种:
The main queue:主队列,主线程就是在个队列中,系统默认就有一个串行队列
Global queues: 全局并发队列
用户队列:是用函数dispatch_queue_create创建的自定义队列
而用户队列又分为下面两种:(1)Serial Dispatch Queue线程池只提供一个线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始。(DISPATCH_QUEUE_SERIAL)
(2)Concurrent Dispatch Queue线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行。(DISPATCH_QUEUE_CONCURRENT)
关于同步和异步
dispatch_sync则调用用 dispatch_sync的线程会等dispatch_sync的对内容执行完再继续执行。dispatch_sync函数不会立即返回,即阻塞当前线程,等待block同步执行完成。
dispatch_async调用dispatch_async的线程不会的等dispatch_async的内容,自己继续执行。dispatch_async函数会立即返回, block会在后台异步执行。
关于并发和并行
并行:是多个任务在同一个时间片段内同时进行,比如说你一边吃饭一边打电话一遍看电视
并发:是多个任务在同一个时间的点上可以切换进行,比如说你先吃着饭,然后电话来了,停下吃饭立马打电话,打完后立马切换到看电视,这个你在同一个线条内只有一个任务在执行
图示关于并发和并行
并行
并发
下面就以代码实例来分析GCD用法
实例一:DISPATCH_QUEUE_SERIAL串行队列
/** DISPATCH_QUEUE_SERIAL是每次运行一个任务,可以添加多个,执行次序FIFO。 **/
- (void)test1{
NSDate *date = [NSDate date];
NSString *daStr = [date description];
const char *queueName = [daStr UTF8String]; //DISPATCH_QUEUE_SERIAL等同于NULL
dispatch_queue_t myQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL);
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(addTime) userInfo:nil repeats:YES];
//在main_queue中异步将任务放到myQueue队列里
//放入myQueue的顺序是按代码的执行顺序:任务一,任务二,任务三(因为main_queue是顺序执行)
//在myQueue取出的顺序是按照FIFO的顺序
//因为这是一个串行的队列,所以取出后的任务是一个接着一个执行的 //执行结果 //[NSThread sleepForTimeInterval:6]~~~~~~~~6 //[NSThread sleepForTimeInterval:3]~~~~~~~~9 //[NSThread sleepForTimeInterval:12]~~~~~~~~21 //任务一: dispatch_async(myQueue, ^{ [NSThread sleepForTimeInterval:6]; NSLog(@"[NSThread sleepForTimeInterval:6]~~~~~~~~%d", t); }); //任务二: dispatch_async(myQueue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"[NSThread sleepForTimeInterval:3]~~~~~~~~%d", t); }); //任务三: dispatch_async(myQueue, ^{ [NSThread sleepForTimeInterval:12]; NSLog(@"[NSThread sleepForTimeInterval:12]~~~~~~~~%d", t); });}-(void)addTime { ++t;}
实例二:全局的并发队列dispatch_get_global_queue
/** 可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务. **/- (void)test2{ dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //在main_queue中异步将任务放到myQueue //放入myQueue的顺序是:任务一,任务二,任务三 //在myQueue中取出的顺序是FIFO的原则:任务一,任务二,任务三 //取出的任务放到线程里执行,因为这是一个并行的队列,所以任务可以同时运行 //执行的结果 //执行的结果可以是 1 2 3 的随机组合 //任务一: dispatch_async(myQueue, ^{ NSLog(@"~~~~~~~~~1"); }); //任务二: dispatch_async(myQueue, ^{ NSLog(@"~~~~~~~~~2"); }); //任务三: dispatch_async(myQueue, ^{ NSLog(@"~~~~~~~~~3"); });}
实例三:DISPATCH_QUEUE_SERIAL串行队列
/** 串行队列的异步任务:使用一个子线程依次执行。 对比一下dispatch_async和dispatch_sync输出的i的顺序和线程的地址 **/- (void)test3{ dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 3; i++) { //在main_queue中将i=0,1,2的任务异步加入queue队列中 //加入queue的顺序是:i=0, i=1, i=2 //所以在queue中取出的顺序是:i=0,i=1,i=2 //因为queue是同步队列,所以三个任务放到同一个线程中依次执行 //运行结果 //~~~i=0~~~~<NSThread: 0x600000076940>{number = 3, name = (null)} //<NSThread: 0x600000068d00>{number = 1, name = (null)} //~~~i=1~~~~<NSThread: 0x600000076940>{number = 3, name = (null)} //<NSThread: 0x600000068d00>{number = 1, name = (null)} // ~~~i=2~~~~<NSThread: 0x600000076940>{number = 3, name = (null)} //<NSThread: 0x600000068d00>{number = 1, name = (null)} //当是使用DISPATCH_QUEUE_SERIAL和dispatch_sync同步的时候,三个任务执行的线程就是当前的mainThread中执行的 dispatch_async(queue, ^{ NSLog(@"~~~i=%d~~~~%@", i, [NSThread currentThread]); NSLog(@"%@",[NSThread mainThread]); }); }}
实例四:DISPATCH_QUEUE_CONCURRENT并发队列
/** 并行队列的异步任务:使用多个子线程无序执行,一般任务较少时几个任务就开几个线程,较多时则开部分线程。 应用:一系列的异步任务没有先后顺序,结束无序 对比一下dispatch_async和dispatch_sync输出的线程的地址 **/- (void)test4{ dispatch_queue_t queue = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT); //在main_queue中异步的将i=0,i=1,i=2的任务加入到queue中 //加入queue的顺序是i=0,i=1,i=2 //在queue中取出的顺序是i=0,i=1,i=2 //因为queue是并发队列,所以有多条的线程来执行三个任务,thread的执行的顺序不定 //当使用dispatch_sync时,执行的顺序又成了i=0,i=1,i=2 for (int i = 0; i < 3; i++) { dispatch_async(queue, ^{ NSLog(@"~~~i=%d~~~~%@", i, [NSThread currentThread]); NSLog(@"%@",[NSThread mainThread]); }); }}
实例五:dispatch_group_t组队列
/** dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。 dispatch_group_async会监听最终的任务完成后,并通知一个线程 这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成了。注意这里不是监听queue里所有的任务完成,而是添加到组里的任务,这个任务是在这个queue里,同时也在这个组里,组里所有的任务的完成并不代表queue里所有的任务的完成。 下面是一段例子代码: 注意:当queue是global(或者DISPATCH_QUEUE_CONCURRENT)队列和DISPATCH_QUEUE_SERIAL队列时线程的区别 **/- (void)test5{ //dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t queue = dispatch_queue_create("queue3"