iOS-线程依赖的处理方法总结

作者: 路飞_Luck | 来源:发表于2019-04-01 20:34 被阅读6次
一 序言

在iOS开发中,我们经常会用到一个线程需要等待另一个结束才能进行的需求,这种需求其实有很实用的解决办法.下面我将列举一些目前用到的两种方式.

一 线程同步
  • 线程同步,字面意思好像是多个线程一起工作.

  • 其实不然,这里的同是协同,互相配合的意思,也就是多个线程互相配合,按照预定顺序依次执行的意思.

  • 我们平时所用的各种锁(比如NSLock,OSSpinLock,NSRecursiveLock)也是线程同步的一部分

  • 系统提供的atomic保证了属性的原子性,但是仅仅只是在gettersetter的时候使用自旋锁,并不是线程安全.比如多个线程对可变数组的增删

二 信号量简介

特性
抽象的来讲,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待)Release(释放)。当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。

  • dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

如果信号量大于等于一时,做减一操作,然后往下执行。否则一直等下去(指放入阻塞队列),直到信号量大于等于一。

  • dispatch_semaphore_signal(semaphore)

释放资源,信号量加一操作

三 GCD

这里使用 GCD 来实现线程依赖

  • 1.GCD 没有添加线程依赖
- (void)gcdDependTest2 {
    // 线程一做事情
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程一做事情");
        for (int i  = 0; i < 10000; i++) {
            //just for delayed
        }
        sleep(1);
        NSLog(@"dispatch1 semaphore send");
    });

    // 线程二做事情
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程二做事情");
        for (int i  = 0; i < 10000; i++) {
            //just for delayed
        }
        sleep(1);
        NSLog(@"dispatch2 semaphore send");
    });

    // 线程二做事情
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程三做事情");
        for (int i  = 0; i < 10000; i++) {
            //just for delayed
        }
        sleep(1);
        NSLog(@"dispatch3 semaphore send");
    });
    
    NSLog(@"function end");
}

运行结果

GCD无依赖.png
  • 2.GCD添加线程使用信号量添加线程依赖实例代码
/// 让线程一 线程二 线程三顺序执行
- (void)gcdDependTest3 {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);  // 信号量初始化为0
    
    // 线程一做事情
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程一做事情");
        for (int i  = 0; i < 10000; i++) {
            //just for delayed
        }
        sleep(1);
        NSLog(@"dispatch1 semaphore send");
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    // 线程二做事情
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程二做事情");
        for (int i  = 0; i < 10000; i++) {
            //just for delayed
        }
        sleep(1);
        NSLog(@"dispatch2 semaphore send");
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    // 线程二做事情
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程三做事情");
        for (int i  = 0; i < 10000; i++) {
            //just for delayed
        }
        sleep(1);
        NSLog(@"dispatch3 semaphore send");
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"function end");
}

运行结果

GCD添加依赖.png

1.通过执行结果可知,通过信号量的确可以给 GCD线程添加依赖。
2.dispatch_semaphore_signal(semaphore)方法必须再另一个线程中调用
3.dispatch_semaphore_wait()一定不能在主线程中调用,因为一不小心就会阻塞当前线程,造成主线程卡死。

四 GCD - dispatch_barrier_async

1.dispatch_barrier_async():dispatch_barrier_sync():使用此方法创建的任务首先会查看队列中有没有别的任务要执行,如果有,则会等待已有任务执行完毕再执行;同时在此方法后添加的任务必须等待此方法中任务执行后才能执行。

2.说明dispatch_barrier_async的顺序执行还是依赖queue的类型,必需要queue的类型为dispatch_queue_create创建的,而且attr参数值必需是DISPATCH_QUEUE_CONCURRENT类型,前面两个非dispatch_barrier_async的类型的执行是依赖其本身的执行时间的,如果attr如果是DISPATCH_QUEUE_SERIAL时,那就完全是符合Serial queue的FIFO特征了。

  • 1.实例代码如下 DISPATCH_QUEUE_CONCURRENT类型
- (void)dispatchBarrierAsyncTest {
    dispatch_queue_t queue = dispatch_queue_create("gcd.barrier.async", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"dispatch_async1");
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async2");
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"dispatch_barrier_async");
        [NSThread sleepForTimeInterval:0.5];
        
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async3");
    });
}

运行结果如下

DISPATCH_QUEUE_CONCURRENT.png
  • 2.实例代码如下 DISPATCH_QUEUE_SERIAL类型
- (void)dispatchBarrierAsyncTest {
    dispatch_queue_t queue = dispatch_queue_create("gcd.barrier.async", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"dispatch_async1");
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async2");
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"dispatch_barrier_async");
        [NSThread sleepForTimeInterval:0.5];
        
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async3");
    });
}

执行结果

DISPATCH_QUEUE_SERIAL.png
  • 3.如果队列采用dispatch_get_global_queue创建,并且参数为DISPATCH_QUEUE_PRIORITY_DEFAULT
- (void)dispatchBarrierAsyncTest {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"dispatch_async1");
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async2");
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"dispatch_barrier_async");
        [NSThread sleepForTimeInterval:0.5];
        
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async3");
    });
}

执行结果如下

image.png
五 NSOperationQueue
  • 实例代码
- (void)nsoperationDependTest {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 1000; i++) {
            
        }
        sleep(1);
        NSLog(@"op1 is finish");
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10000; i++) {
            
        }
        sleep(1);
        NSLog(@"op2 is finish");
    }];
    [op1 addDependency:op2];
    [queue addOperation:op1];
    [queue addOperation:op2];
}

执行结果

NSOperationQueue.png

本文参考
ios 几种线程依赖的处理方式
GCD之dispatch queue深入浅出


项目连接地址 - ThreadDepend

相关文章

  • iOS-线程依赖的处理方法总结

    一 序言 在iOS开发中,我们经常会用到一个线程需要等待另一个结束才能进行的需求,这种需求其实有很实用的解决办法....

  • iOS底层原理——浅谈RunLoop

    RunLoop应用:线程保活 线程保活、控制销毁 iOS-浅谈RunLoop8iOS底层原理总结 - RunLoo...

  • dispatch_group示例

    应用场景 当前线程依赖其他线程的执行结果 需要依赖多个线程执行完成后的结果,并及时处理

  • 线程同步

    线程锁(同时只允许一个线程访问) 条件锁,(可以锁线程,线程依赖) wait方法:等待信号signal方法:发送信...

  • iOS 多线程之间有依赖时的处理方式

    iOS 多线程之间有依赖时的处理方式

  • Android消息机制分析

    异步消息处理线程 对于普通的线程而言,执行完run()方法内的代码后线程就结束。而异步消息处理线程是指,线程启动后...

  • 常用gcd-dispatch_async

    线程异步处理后,主线程再处理 延时处理 判断三个并发队列执行完毕的方法dispatch_group_notify ...

  • iOS-多线程-总结

    容易混淆的概念(队列、线程、串行并行、同步异步、任务) 任务:代码块(一行或多行),也叫block队列:管理任务的...

  • 多线程篇

    因为performSelector方法是需要依赖线程runLoop,但是开辟的子线程默认是没有开启runLoop的...

  • Android多线程界面更新方法的总结

    Android多线程界面更新的方法总结 Android多线程与界面交互的方法 Activity.runOnUiTh...

网友评论

    本文标题:iOS-线程依赖的处理方法总结

    本文链接:https://www.haomeiwen.com/subject/klybbqtx.html