- 基本概念
了解多线程之前,首先我们要对操作系统关于多线程方面的名词解释一下,学过操作系统课程的同学可以跳过。
- 进程: 一个具有一定独立功能的程序关于某个数据集合的一次运行活动。可以理解成一个运行中的APP。
- 线程:程序能够进行运算调度的最小单元,一个进程可以包含多个线程。
- 同步:只能在当前线程按先后顺序依次执行,不开启新线程,在完成了它预定的任务后才返回。
- 异步:可以在当前线程开启多个新线程执行,执行的顺序无法保证,会立即返回,预定的任务会完成但不会等它完成。因此,一个异步函数不会阻塞当前线程去执行下一个函数。
- 并行:对于多核CPU来说,可以在同一时间执行多个任务。对于单核的CPU来说,是在很短的时间片段轮询执行,让人感觉是同时执行。
- 串行:线程执行只能依次逐一先后有序的执行。
- 队列:GCD 提供有
dispatch queues
来处理代码块,这些队列管理你提供给 GCD 的任务并用 FIFO 顺序执行这些任务。这就保证了第一个被添加到队列里的任务会是队列中第一个开始的任务,而第二个被添加的任务将第二个开始,如此直到队列的终点。队列分为串行队列和并行队列,串行队列(Serial Queus)串行队列中的任务一次执行一个,每个任务只在前一个任务完成时才开始。而且,你不知道在一个 Block 结束和下一个开始之间的时间长度。并发队列(Concurrent Queus):在并发队列中的任务能得到的保证是它们会按照被添加的顺序开始执行,任务可能以任意顺序完成,你不会知道何时开始运行下一个任务,或者任意时刻有多少 Block 在运行。
- 队列和任务的特点
队列以先进先出的方式进行任务的调度,当轮到某个任务执行的时候,从队列取出,交给一个线程去执行。
- 串行队列:任务按照添加的顺序被调度,当前任务不执行完毕,不会进行下次任务的调度。
- 并行队列:只要有空闲的线程,队列就会调度当前任务,交给线程去执行,不需要考虑前面是都有任务在执行,只要有线程可以利用,队列就会调度任务。
- main队列:是一个串行队列,主要用于UI更新相关的任务。
- global队列:全局队列是一个并行队列。
- 同步任务:不会开新线程,任务一个接着一个执行。
- 异步任务:会新开线程,任务可以并发执行。
- 任务和队列组合
- 串行队列同步任务:组合特点串行队列顺序执行,同步任务不会开新线程,所以是one-by-one。
dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.demo", DISPATCH_QUEUE_SERIAL); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"任务1"); }); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"任务2"); }); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"任务3"); }); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"任务4"); });
打印如下:
2016-08-04 15:42:53.334 GCDTestDemo[6858:1582521] 任务1
2016-08-04 15:42:54.342 GCDTestDemo[6858:1582521] 任务2
2016-08-04 15:42:56.348 GCDTestDemo[6858:1582521] 任务3
2016-08-04 15:42:57.353 GCDTestDemo[6858:1582521] 任务4
2.串行队列异步任务:串行队列当前任务没有结束,不会进行下次调度。异步任务会在另一个线程上one-by-one的执行。
执行结果如下
3.并行队列同步任务:并行队列不用等待当前任务,只要有线程,就会调度下一个任务,同步任务不会开启新的线程,所以组合后还是one-by-one的执行。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self testSerialQueue]; NSLog(@"%@",[NSThread currentThread]); });
dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.demo", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"任务1 %@",[NSThread currentThread]); }); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"任务2 %@",[NSThread currentThread]); }); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"任务3 %@",[NSThread currentThread]); }); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"任务4 %@",[NSThread currentThread]); });
执行结果:
2016-08-04 18:03:52.823 GCDTestDemo[7064:1618448] 任务1 <NSThread: 0x14d47590>{number = 3, name = (null)}
2016-08-04 18:03:53.827 GCDTestDemo[7064:1618448] 任务2 <NSThread: 0x14d47590>{number = 3, name = (null)}
2016-08-04 18:03:55.832 GCDTestDemo[7064:1618448] 任务3 <NSThread: 0x14d47590>{number = 3, name = (null)}
2016-08-04 18:03:58.838 GCDTestDemo[7064:1618448] 任务4 <NSThread: 0x14d47590>{number = 3, name = (null)}
2016-08-04 18:03:58.838 GCDTestDemo[7064:1618448] <NSThread: 0x14d47590>{number = 3, name = (null)}
4.并行队列异步任务:再上一条中说明了并行队列的特点,而异步执行是任务可以开启新的线程,所以这中组合可以实现任务的并发,再实际开发中也是经常会用到的。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self testSerialQueue]; NSLog(@"%@",[NSThread currentThread]); });
- (void)testSerialQueue { dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.demo", DISPATCH_QUEUE_CONCURRENT); dispatch_async(serialQueue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"任务1 %@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"任务2 %@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"任务3 %@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"任务4 %@",[NSThread currentThread]); }); }
执行结果:
2016-08-04 18:07:39.746 GCDTestDemo[7069:1619191] <NSThread: 0x15e56cf0>{number = 2, name = (null)}
2016-08-04 18:07:40.752 GCDTestDemo[7069:1619197] 任务2 <NSThread: 0x15d7dd00>{number = 3, name = (null)}
2016-08-04 18:07:41.752 GCDTestDemo[7069:1619196] 任务3 <NSThread: 0x15d514f0>{number = 4, name = (null)}
2016-08-04 18:07:42.751 GCDTestDemo[7069:1619198] 任务1 <NSThread: 0x15d79b90>{number = 5, name = (null)}
2016-08-04 18:07:42.752 GCDTestDemo[7069:1619199] 任务4 <NSThread: 0x15d644e0>{number = 6, name = (null)}
- GCD常用的方法介绍。
-
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
提交一个异步任务到当前队列。
2. dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
提交一个同步任务到当前队列。
3. dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
创建自定义队列,label:队列标示;attr:队列类型,DISPATCH_QUEUE_CONCURRENT(并行队列),DISPATCH_QUEUE_SERIAL(串行队列)。
4. dispatch_get_global_queue(long identifier, unsigned long flags);
获取全局队列,identifier:队列的优先级,一般是DISPATCH_QUEUE_PRIORITY_DEFAULT,flags:一般是0
5. dispatch_get_main_queue()
获取Main队列,更新UI使用。
/** * 延迟执行 * * @param DISPATCH_TIME_NOW 开始时间 * @param int64_t 延迟的时间 * * @return */ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"time"); }); /** * 执行一次,经常用于单利的实现 * * @param predicate * @param void * * @return */ dispatch_once_t predicate; dispatch_once(&predicate, ^{ }); dispatch_queue_t queue; /** * 控制并发队列中任务的执行顺序,在此函数前面添加的任务,都执行完毕之后,执行此次任务。之后添加的任务也会等待当天任务执行完毕在执行。 * * @param queue <#queue description#> * @param void <#void description#> * * @return 函数立马返回 和 dispatch_barrier_sync的区别是,dispatch_barrier_sync不马上返回而是等任务执行结束 */ dispatch_barrier_async(queue, ^{ }); /** * 循环执行任务 * * @param iterations#> 次数 description#> * @param queue#> 队列 description#> * @param size_t index * * @return */ dispatch_apply(10, queue, ^(size_t index) { });
//0.创建队列组 dispatch_group_t group = dispatch_group_create(); //1.创建队列 dispatch_queue_t queue = dispatch_queue_create("com.gcd.demo", DISPATCH_QUEUE_CONCURRENT); //2.异步函数,在队列组上执行异步任务 dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"1---%@",[NSThread currentThread]); }); dispatch_group_async(group, queue, ^{ NSLog(@"2---%@",[NSThread currentThread]); }); dispatch_group_async(group, queue, ^{ NSLog(@"3---%@",[NSThread currentThread]); }); //3.当队列组中所有的任务都执行完毕之后会通知group执行dispatch_group_notify方法 dispatch_group_notify(group, queue, ^{ NSLog(@"队列组中所有的任务都执行完毕了"); });
- 扩展
block1任务花费5秒,block2任务花费10秒。serialQueue(串行队列),concurrentQueue(并行队列)。同步任务执行sync,异步任务执行async.
1.sync(concurrentQueue,block1);sync(concurrentQueue,block2),求花费了多少时间?
2.sync(concurrentQueue,block1);async(concurrentQueue,block2), 求花费了多少时间?
3.sync(serialQueue,block1);async(serialQueue,block2), 求花费了多少时间?
对于上述题目,异步任务马上返回耗费时间可以忽略,只计算同步任务花费的时间即可。