在多线程开发中,我们经常会利用到多线程的特性并发执行功能,用以提高执行的效率。那么在这种并发执行中如何来控制特定任务的执行顺序呢?
1. 使用栅栏函数
栅栏函数可以在指定的队列中设置障碍,来阻碍后续任务的执行,从而达到控制执行顺序的目的.假设有四个任务,我需要任务三在任务一,任务二完成之后执行,任务四在任务三完成之后执行:
dispatch_queue_t queue = dispatch_queue_create("com.people'srepublicofChina.ROC", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"task 001 ---- %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"task 002 ---- %@", [NSThread currentThread]); }); dispatch_barrier_sync(queue, ^{ NSLog(@"task 003 ---- %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"task 004 ---- %@", [NSThread currentThread]); });
2. 使用任务组
任务组可以方便添加一组相关的多个操作,然后控制各个任务之间的执行依赖:
- dispatch_group_create() :用来创建一个任务组;
- dispatch_group_async(group, queue, block):将任务异步添加到任务组中;
- dispatch_group_sync(group, queue, block):将任务同步添加到任务组中;
- dispatch_group_enter(group),需要与dispatch_group_leave(group)成对出现配合使用,也是一种将任务添加到任务组的一种方式,只不过该种方式可以手动控制任务执行完成的标志,可以用于一些特殊的场景,比如相互依赖的异步请求;
- dispatch_group_wait(group, time):在阻塞group中任务,需要将之前的任务都完成才能继续该函数之后的任务;
- dispatch_group_notify(group, queue, block):在group中的任务都完成以后开始执行block,相当于一种任务执行完成之后的完成回调.
假设有四个任务,我需要任务三在任务一,任务二完成之后执行,任务四在任务三完成之后执行:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSLog(@"task 001 ---- %@", [NSThread currentThread]); }); dispatch_group_async(group, queue, ^{ NSLog(@"task 002 ---- %@", [NSThread currentThread]); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); dispatch_group_async(group, queue, ^{ NSLog(@"task 003 ---- %@", [NSThread currentThread]); }); dispatch_group_notify(group, queue, ^{ dispatch_async(queue, ^{ NSLog(@"task 004 ---- %@ \n", [NSThread currentThread]); }); });
3. 使用NSOperationQueue
NSOperation,NSOperationQueue是一种基于GCD的对象封装,更接近OC的语言风格,同时也更加灵活。相比较于GCD而言,这套API具有以下优点:
- 添加操作之间的依赖关系,方便的控制执行顺序;
- 设定操作执行的优先级;
- 可以很方便的取消一个操作的执行;
- 使用 KVO 观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled等。
假设有四个任务,我需要任务三在任务一,任务二完成之后执行,任务四在任务三完成之后执行。
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"task 001 ---- %@", [NSThread currentThread]); }]; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"task 002 ---- %@", [NSThread currentThread]); }]; NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"task 003 ---- %@", [NSThread currentThread]); }]; NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"task 004 ---- %@", [NSThread currentThread]); }]; [operation3 addDependency:operation1]; [operation3 addDependency:operation2]; [operation4 addDependency:operation3]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[operation1, operation2, operation3, operation4] waitUntilFinished:false];
4. 使用锁
锁的出现主要是为了防止多线程开发中的资源竞争问题,确保在一个时间点只能有一个线程访问资源,可以用来对任务流进行依赖控制,只不过对于锁特性而言是有局限性的,只能控制串行的事件顺序,不能控制并行的执行.例如我们可以使用NSConditionLock来实现不同线程之间的事件执行顺序:
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0]; dispatch_queue_t queue = dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT); dispatch_async(queue, ^{ [lock lockWhenCondition:2]; NSLog(@"task 002 ---- %@", [NSThread currentThread]); [lock unlockWithCondition:3]; }); dispatch_async(queue, ^{ [lock lockWhenCondition:3]; NSLog(@"task 003 ---- %@", [NSThread currentThread]); [lock unlockWithCondition:4]; }); dispatch_async(queue, ^{ [lock lockWhenCondition:0]; NSLog(@"task 001 ---- %@", [NSThread currentThread]); [lock unlockWithCondition:2]; });
输出结果:
task 001 ---- <NSThread: 0x282c4f140>{number = 3, name = (null)} task 002 ---- <NSThread: 0x282c48240>{number = 4, name = (null)} task 003 ---- <NSThread: 0x282c41600>{number = 5, name = (null)}
NSConditionLock在初始化是有一个condition值,只有到加锁时condition与初始化值相等时才可以成功加锁,而其他的未能加锁成功的线程则会阻塞线程执行,等待condition值变化时继续尝试加锁.在上述操作中,初始化的condition=0,所以只有task 0011可以加锁成功,其余锁阻塞线程.等待task 0011执行结束,解锁并将condition=2,此时task 002加锁成功,但是task 003依旧失败,继续阻塞线程.等待task 002执行结束,解锁并将condition=3,此时task 003加锁成功,执行task 003.