控制任务的执行顺序的常用技术

在多线程开发中,我们经常会利用到多线程的特性并发执行功能,用以提高执行的效率。那么在这种并发执行中如何来控制特定任务的执行顺序呢?

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.

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值