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-线程依赖的处理方法总结

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