美文网首页
iOS中常见锁

iOS中常见锁

作者: PursueDream | 来源:发表于2018-05-12 14:49 被阅读0次

    pthread_mutex

    POSIX threads(简称Pthreads)定义了一套跨平台的多线程常用API,线程同步在并行编程中非常重要的,其中最典型的应用就是用Pthreads提供的锁机制来对多个线程之间共享临界区进行保护。

    Pthreads锁的常见用法;

    pthread_mutexattr_t attr;  
    pthread_mutexattr_init(&attr);  //初始化属性
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);  // 定义锁的属性,定义成常规锁
    pthread_mutex_t mutex;  
    pthread_mutex_init(&mutex, &attr) // 创建锁
    pthread_mutex_lock(&mutex); // 申请锁  
        // 临界区代码
    pthread_mutex_unlock(&mutex); // 释放锁 
    

    从上面的代码中我们很容易就明白其用法这里不在详述,我们下面着重对锁的类型进行介绍。在方法pthread_mutexattr_settype中,我们将第二个参数设置为PTHREAD_MUTEX_NORMAL,这是将锁设置为普通锁,不做死锁检测。有可能造成死锁,实际上它还可以有其他几种类型。

    1. PTHREAD_MUTEX_NORMAL
      如果mutex的type被置为PTHREAD_MUTEX_NORMAL,则系统将不会为他提供死锁检测,尝试对已经加锁的mutex进行加锁操作的时候,将会造成死锁。如果一个线程试图解锁一个还没有加锁或者已经被解锁的mutex进行解锁操作的时候,会发生不可预测问题。
    2. PTHREAD_MUTEX_ERRORCHECK
      如果mutex的type被置为PTHREAD_MUTEX_ERRORCHECK,则系统将会为他提供错误检测。尝试对已经加锁的mutex进行加锁操作的时候会返回错误。如果一个线程试图解锁一个还没有加锁或者已经被解锁的mutex进行解锁操作的时候,也会返回错误。
    3. PTHREAD_MUTEX_RECURSIVE(递归锁)
      如果mutex的type被置为PTHREAD_MUTEX_RECURSIVE,mutex会维护一个加锁次数的变量。当一个线程第一次成功给mutex加锁后,加锁次数将被设置为1.线程每次对mutex加锁,加锁次数加1,每次解锁加锁次数减1,当加锁次数变为0时,其他线程就可以对mutex加锁了。如果一个线程试图解锁一个还没有加锁或者已经被解锁的mutex进行解锁操作的时候,会返回错误。
    4. PTHREAD_MUTEX_DEFAULT
      如果mutex的type被置为PTHREAD_MUTEX_DEFAULT,尝试递归的给mutex加锁会导致不可预知的错误,如果一个线程试图解锁一个还没有加锁或者已经被解锁的mutex进行解锁操作的时候也会返回不可预知的错误。

    其中PTHREAD_MUTEX_NORMAL,PTHREAD_MUTEX_ERRORCHECK,PTHREAD_MUTEX_DEFAULT定义的是互斥锁,PTHREAD_MUTEX_RECURSIVE定义的是递归锁。

    一般情况下,一个线程只能申请一次锁,也只能在获得锁的情况下才能释放锁,多次申请锁或释放未获得的锁都会导致不可预知错误。假设在已经获得锁的情况下再次申请锁,线程会因为等待锁的释放而进入睡眠状态,因此就不可能再释放锁,从而导致死锁。

    然而这种情况经常会发生,比如某个函数申请了锁,在临界区内又递归调用了自己,递归锁就可以解决这种问题。递归锁的简单实现可以看这里

    自旋锁

    自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

    从实现原理上来讲,Mutex属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0和 Core1上。假设线程A想要通过
    pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞(blocking),Core0会在此时进行上下文切换(Context Switch)将线程A置于
    等待队列中,此时Core0就可以运行其他的任务(例如另一个线程C)而不必进行忙等待。而Spin lock则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止。

    因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。虽然它的效率比互斥锁高,但是它也有些不足之处,自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。

    自旋锁的简单实现逻辑可以查看这里

    自旋锁

    参考资料:
    1.https://bestswifter.com/ios-lock/
    2.https://www.cnblogs.com/zendu/p/5387596.html
    3.http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_mutex_lock.html
    4.http://blog.sina.com.cn/s/blog_7c6086150101a30y.html

    相关文章

      网友评论

          本文标题:iOS中常见锁

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