OSSpinLock 的替代方案: dispatch_semaphore

iOS 实现线程加锁有不少种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/get等等各类方式。这里不对其余各类锁做介绍,只介绍dispatch_semaphore使用。ios

至于为何使用dispatch_semaphore及它的性能如何 , 可参考 再也不安全的 OSSpinLock安全

  • 下图是各类方式性能对比:

各个lock性能对比.png

定义:

  • 1> 信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问以前,加上信号量的处理,则可告知系统按照咱们指定的信号量数量来执行多个线程。

其实,这有点相似锁机制了,只不过信号量都是系统帮助咱们处理了,咱们只须要在执行线程以前,设定一个信号量值,而且在使用时,加上信号量处理方法就好了。bash

  • 2> 信号量主要有3个函数,分别是:
// 建立信号量 . 该函数接收一个long类型的参数, 返回一个dispatch_semaphore_t类型的信号量,值为传入的参数
dispatch_semaphore_t   dispatch_semaphore_create(long value)

//等待下降信号量 . 接收一个信号和时间值,若信号的信号量为0,则会阻塞当前线程,直到信号量大于0或者通过输入的时间值;若信号量大于0,则会使信号量减1并返回,程序继续住下执行
long  dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)

// 提升信号量.  使信号量加1并返回
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)

//dispatch_semaphore_wait() 与 dispatch_semaphore_signal() 成对使用
复制代码

dispatch_semaphore 简单使用

- (void)dispatchSignal{
    //crate的value表示,最多几个资源可访问
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);   
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     
    //任务1
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 1");
        sleep(1);
        NSLog(@"complete task 1");
        dispatch_semaphore_signal(semaphore);       
    });<br>
    //任务2
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 2");
        sleep(1);
        NSLog(@"complete task 2");
        dispatch_semaphore_signal(semaphore);       
    });<br>
    //任务3
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 3");
        sleep(1);
        NSLog(@"complete task 3");
        dispatch_semaphore_signal(semaphore);       
    });   
}

复制代码
  • 执行结果:

log1

总结:因为设定的信号值为2,先执行两个线程,等执行完一个,才会继续执行下一个,保证同一时间执行的线程数不超过2。异步

  • 这里咱们扩展一下,假设咱们设定信号值=1
dispatch_semaphore_create(1)
复制代码

那么结果就是:async

log2.png

  • 若是设定信号值=3
dispatch_semaphore_create(3)
复制代码

那么结果就是:  函数

log3.png

其实设定为3,就是不限制线程执行了,由于一共才只有3个线程。性能

dispatch_semaphore 做用

  • dispatch_semaphore有两个主要应用 :
  1. 保持线程同步
  2. 为线程加锁

dispatch_semaphore 保持线程同步

// dispatch_semaphore 保持线程同步
- (void)syncThread{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block int j = 0;
    dispatch_async(queue, ^{
        j = 100;
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"finish j = %d", j);
}
复制代码

结果输出 j = 100; 若是注释掉dispatch_semaphore_wait这一行,则 j = 0;ui

原理: 因为是将block异步添加到一个并行队列里面,因此程序在主线程跃过block直接到dispatch_semaphore_wait这一行,由于semaphore信号量为0,时间值为DISPATCH_TIME_FOREVER,因此当前线程会一直阻塞,直到block在子线程执行到dispatch_semaphore_signal,使信号量+1,此时semaphore信号量为1了,因此程序继续往下执行。这就保证了线程间同步了。atom

dispatch_semaphore 线程加锁

// dispatch_semaphore 线程加锁
- (void)lockThread{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
    for (int i = 0; i < 100; i++) {
        dispatch_async(queue, ^{
            // 至关于加锁
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"i = %d semaphore = %@", i, semaphore);
            // 至关于解锁
            dispatch_semaphore_signal(semaphore);
        });
    }
}

复制代码

原理: 当线程1执行到dispatch_semaphore_wait这一行时,semaphore的信号量为1,因此使信号量-1变为0,而且线程1继续往下执行;若是当在线程1NSLog这一行代码还没执行完的时候,又有线程2来访问,执行dispatch_semaphore_wait时因为此时信号量为0,且时间为DISPATCH_TIME_FOREVER,因此会一直阻塞线程2(此时线程2处于等待状态),直到线程1执行完NSLog并执行完dispatch_semaphore_signal使信号量为1后,线程2才能解除阻塞继续住下执行。以上能够保证同时只有一个线程执行NSLog这一行代码。spa

相关文章
相关标签/搜索