iOS面试知识点iOS底层基础知识将来跳槽用

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

2019-04-01  本文已影响6人  路飞_Luck
一 序言

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

一 线程同步
二 信号量简介

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

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

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

三 GCD

这里使用 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
/// 让线程一 线程二 线程三顺序执行
- (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特征了。

- (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
- (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
- (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

上一篇下一篇

猜你喜欢

热点阅读