zoukankan      html  css  js  c++  java
  • Linux 自旋锁,互斥量(互斥锁),读写锁

    自旋锁(Spin Lock)

    自旋锁类似于互斥量,不过自旋锁不是通过休眠阻塞进程,而是在取得锁之前一直处于忙等待的阻塞状态。这个忙等的阻塞状态,也叫做自旋。

    自旋锁通常作为底层原语实现其他类型的锁。

    适用场景:

    1)锁被持有的时间短,而且线程不希望在重新调度上花费太多的成本;
    2)在非抢占式内核中,会阻塞中断,这样中断处理程序不会让系统陷入死锁状态。因为中断处理程序无法休眠,只能使用这种锁;

    缺点:

    1)线程自旋等待锁变成可用时,CPU不能做其他事情,会浪费CPU资源;

    伪代码

    S = 1
    
    线程P: 
    // 进入区
    while (S <= 0) ; // 自旋
    S--; // P操作
    
    ... // 临界区
    
    // 退出区
    S++; // V操作
    

    自旋锁接口

    自旋锁接口与互斥量类似,容易相互替换。

    #include <pthread.h>
    
    int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
    
    int pthread_spin_destroy(pthread_spinlock_t *lock);
    
    int pthread_spin_lock(pthread_spinlock_t *lock);
    int pthread_spin_trylock(pthread_spinlock_t *lock);
    int pthread_spin_unlock(pthread_spinlock_t *lock);
    

    注意:
    1)如果自旋锁当前在解锁状态,pthread_spin_lock不用自旋,就可以对它加锁;
    2)如果自旋锁当前在加锁状态,再获得锁的结果是未定义的。如果调用pthread_spin_lock,会返回EDEADLK错误或其他错误,或者调用者可能会永久自旋。取决于具体实现。
    3)试图对没有加锁的自旋锁解锁,结果也是未定义的。

    示例

    自旋锁使用

    #include <pthread.h>
    #include <stdio.h>
    
    #define THREAD_NUM 100
    
    pthread_spinlock_t  spinlock;
    
    void *thread_main(void *arg)
    {
        int id = (int)arg;
    
        pthread_spin_lock(&spinlock); // 获得锁
        printf("thread main %d get the lock begin
    ", id);
        printf("thread main %d get the lock end
    ", id);
    
        pthread_spin_unlock(&spinlock); // 释放锁
        return NULL;
    }
    
    int main()
    {
        pthread_spin_init(&spinlock, 0); /* PTHREAD_PROCESS_PRIVATE == 0*/
    
        int x = PTHREAD_PROCESS_PRIVATE;
        printf("x = %d
    ", x);
    
        int i;
        pthread_t tids[THREAD_NUM];
        for (i = 0; i < THREAD_NUM; i++) {
            pthread_create(&tids[i], NULL, thread_main, i); // 创建线程
        }
    
        for (i = 0; i < THREAD_NUM; i++) {
            pthread_join(tids[i], NULL); // 连接线程
        }
    
        return 0;
    }
    

    互斥量(互斥锁, Mutex)

    互斥量(Mutex)通过休眠阻塞进程/线程,确保同一时间只有一个线程访问数据。休眠,也就意味着会放弃CPU资源。

    加锁
    对互斥量加锁后,任何其他试图再次对互斥量加锁的线程,都会被阻塞,直到当前线程释放该互斥锁。

    解锁
    如果阻塞在该互斥锁上的线程有多个,当锁可用时,所有线程都会变成可运行状态,第一个变为运行的线程,就可以对互斥量加锁,其他线程则再次等待锁而进入休眠。

    适用场景

    多线程或多进程运行环境,需要对临界区资源进行保护时。

    缺点

    1)使用不当,任意导致死锁;
    2)无法表示临界区资源可用数量(由信号量解决);

    接口

    #include <pthread.h>
    
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); // 函数方式初始化,attr是线程属性
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 直接赋值方式初始化
    
    int pthread_mutex_lock(pthread_mutex_t *mutex); // 加锁
    int pthread_mutex_trylock(pthread_mutex_t *mutex); // 尝试加锁,不会阻塞
    int pthread_mutex_unlock(pthread_mutex_t *mutex); // 解锁
    

    使用示例

    #define THREAD_NUM 100
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    void *thread_main(void *arg)
    {
        int id = (int)arg;
    
        pthread_mutex_lock(&mutex);
        printf("thread main %d get the lock begin
    ", id);
        printf("thread main %d get the lock end
    ", id);
    
        pthread_mutex_unlock(&mutex);
        return NULL;
    }
    
    int main()
    {
        int i;
        pthread_t tids[THREAD_NUM];
        for (i = 0; i < THREAD_NUM; i++) {
            pthread_create(&tids[i], NULL, thread_main, i);
        }
    
        for (i = 0; i < THREAD_NUM; i++) {
            pthread_join(tids[i], NULL);
        }
    
        return 0;
    }
    

    读写锁(Read-Write Lock)

    读写锁类似于互斥量,不过读写锁允许更高的并行性。读写锁,也叫共享互斥锁(shared-exclusive lock)。

    当读写锁以读模式锁住时,可以说成是以共享模式锁住的。当以写模式锁住时,可以说成是以互斥模式锁住的。

    读写锁与互斥锁的区别

    读写锁与互斥锁的区别在于:
    互斥锁 要么是加锁状态,要么是不加锁状态,而且一次只有一个线程能取得锁、对其加锁;
    读写锁 可以有3种状态:读模式加锁,写模式加锁,不加锁。一次只有一个线程能占有写模式的读写锁,不过多个线程可以同时占有读模式的读写锁。

    1)当读写锁是写加锁状态时,在被解锁前,所有试图对其加锁的线程都会被阻塞。
    2)当读写锁是读加锁状态时,在被解锁前,所有以读模式加锁的线程都可以得到访问权,以写模式加锁的线程会被阻塞。

    简而言之,读写锁是读状态与读状态之间共享,与写状态之间互斥,写状态是与任何状态互斥。
    互斥锁是只有加锁和解锁状态,加锁状态之间互斥。

    适用场景

    读写锁非常适合对数据结构进行读操作的次数 远大于写的情况。

    使用接口

    初始化销毁:

    #include <pthread.h>
    
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
          const pthread_rwlockattr_t *restrict attr);
    
    pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 直接赋值方式初始化读写锁
    

    读、写模式获得锁,解锁:

    #include <pthread.h>
    
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 读模式取得锁
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); // 读模式取得锁的条件版本
    
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); // 写模式取得锁
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 写模式取得锁的条件版本
    
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 解锁
    

    注意:
    1)不论是处于写模式,还是读模式,都可以用pthread_rwlock_unlock解锁。
    2)条件版本不会阻塞线程。

    读写锁属性

    attr = NULL,表示使用默认的读写锁属性:PTHREAD_PROCESS_PRIVATE,表示只在单个进程内的不同线程间共享。另外,还支持属性PTHREAD_PROCESS_SHARED,表示读写锁将在不同进程间共享。

    要设置非默认属性,就要使用下面2个函数初始化、销毁读写锁属性。

    #include <pthread.h>
    
    int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
    int pthread_rwlockattr_destory(pthread_rwlockattr_t *attr);
    

    获取、设置非默认属性:

    #include <pthread.h>
    
    int pthread_rwlockattr_getshared(pthread_rwlockattr_t *attr, int *valptr);
    int pthread_rwlockattr_setshared00(pthread_rwlockattr_t *attr, int value);
    

    要设置的当前值value,其值只能是PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED。

  • 相关阅读:
    Telink TLSR825x系列SDK增加串口打印log功能
    Telink BDT和烧录器EVK的使用
    Telink SDK IDE开发环境中函数链接跳转异常和实际编译项目显示灰色问题的解决
    Telink SDK 的编译设置(以825x系列的一个SDK为例)
    Telink IDE的下载和SDK的导入
    Git项目管理出现 .gitignore文件不起作用的解决
    char和signed char不同编译器下的使用反思
    动态内存的分配
    结构体和联合体
    C语言中利用clock设计一个简单的定时器
  • 原文地址:https://www.cnblogs.com/fortunely/p/15211775.html
Copyright © 2011-2022 走看看