POSIX 自旋锁

更新时间:
2024-05-29
下载文档

POSIX 自旋锁

自旋锁是为实现保护共享资源而提出一种轻量级的锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对共享资源的互斥访问。

无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个拥有者,也就说,在任何时刻最多只能有一个线程获得锁。但是两者在调度机制上有很大的不同。对于互斥锁,如果互斥锁已经被占有,申请者只能进入睡眠状态。但是自旋锁不会引起申请者睡眠,如果自旋锁已经被别的线程占有,申请者就一直在那里循环判断该自旋锁的拥有者是否已经释放该锁,“自旋”一词就是因此而得名。

由此我们可以看出,自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁机制可能存在以下两个问题:

  • 死锁:申请者试图递归地获得自旋锁必然会引发死锁。
  • 消耗过多 CPU 资源 :如果申请不成功,申请者将一直循环判断,这无疑降低了 CPU 的使用率。

由此可见,自旋锁适用于锁使用者保持锁时间比较短的情况。信号量适用于保持锁时间较长的情况。

需要注意的是,被自旋锁保护的临界区代码执行时不能因为任何原因放弃 CPU,因此,在自旋锁保护的区域内不能调用任何可能引发系统任务调度的 API。

POSIX 自旋锁的类型为 pthread_spinlock_t。使用时定义一个 pthread_spinlock_t 类型的变量,如:

pthread_spinlock_t spin;

一个 POSIX 自旋锁必须要调用 pthread_spin_init 函数初始化之后才能使用。

线程如果需要等待一个自旋锁,可以调用 pthread_spin_lock 函数,中断服务程序不可以调用任何 POSIX 自旋锁 API。释放一个自旋锁可以调用 pthread_spin_unlock 函数。

当一个自旋锁使用完毕后(并确保以后也不再使用),应该调用 pthread_spin_destroy 函数将其删除,SylixOS 会回收该自旋锁占用的内核资源。

自旋锁

  1. 自旋锁的初始化和删除

    #include <pthread.h>
    int    pthread_spin_init(pthread_spinlock_t  *pspinlock, int  pshare);
    

    函数 pthread_spin_init 原型分析:

    • 此函数成功时返回 0,失败时返回错误号。
    • 参数 pspinlock 是 POSIX 自旋锁的指针。
    • 参数 pshare 标识了 POSIX 自旋锁是否进程共享。
    #include <pthread.h>
    int    pthread_spin_destroy(pthread_spinlock_t  *pspinlock);
    

    函数 pthread_spin_destroy 原型分析:

    • 此函数成功时返回 0,失败时返回错误号。
    • 参数 pspinlock 是 POSIX 自旋锁的指针。
  2. 自旋锁的加锁与解锁

    #include <pthread.h>
    int    pthread_spin_lock(pthread_spinlock_t  *pspinlock);
    int    pthread_spin_unlock(pthread_spinlock_t  *pspinlock);
    int    pthread_spin_trylock(pthread_spinlock_t  *pspinlock);
    

    以上函数原型分析:

    • 函数成功时返回 0,失败时返回错误号。
    • 参数 pspinlock 是 POSIX 自旋锁的指针。

    pthread_spin_trylock 是 pthread_spin_lock 的“尝试等待”版本,在 POSIX 自旋锁已经被占有时,pthread_spin_lock 将“自旋”,而 pthread_spin_trylock 将立即返回。

    这时,POSIX 自旋锁的解锁操作只能调用 pthread_spin_unlock 函数。

    如果中断服务程序也可能使用 POSIX 自旋锁保护的资源,应该使用带中断屏蔽版本的 POSIX 自旋锁 API,否则会发生死锁:

    #include <pthread.h>
    int    pthread_spin_lock_irq_np(pthread_spinlock_t  *pspinlock, 
                                    pthread_int_t       *irqctx);
    int    pthread_spin_unlock_irq_np(pthread_spinlock_t  *pspinlock, 
                                      pthread_int_t        irqctx);
    int    pthread_spin_trylock_irq_np(pthread_spinlock_t  *pspinlock, 
                                       pthread_int_t       *irqctx);
    

    以上几个函数原型分析:

    • 函数成功时返回 0,失败时返回错误号。
    • 参数 pspinlock 是 POSIX 自旋锁的指针。
    • 参数 irqctx 是 pthread_int_t 类型的指针,用于保存和恢复 CPU 的中断屏蔽寄存器。

    pthread_spin_trylock_irq_np 是 pthread_spin_lock_irq_np 的“尝试等待”版本,在 POSIX 自旋锁已经被占有时,pthread_spin_lock_irq_np 将“自旋”,而 pthread_spin_trylock_irq_np 将立即返回。

    这时,POSIX 自旋锁的解锁操作只能调用 pthread_spin_unlock_irq_np 函数。

    下面程序展示了 POSIX 自旋锁的使用,程序创建两个线程和一个 POSIX 自旋锁,两个线程分别对变量 count 进行打印和自增操作,使用 POSIX 自旋锁作为访问变量 count 的互斥手段。

    #include <stdio.h>
    #include <pthread.h>
    
    static pthread_spinlock_t    lock;
    static int                   count = 0;
    
    static void *thread_a (void *arg)
    {
        int value;
    
        while (1) {
            pthread_spin_lock(&lock);
            value = count;
            pthread_spin_unlock(&lock);
            printf("thread_a(): count = %d\n", value);
            sleep(1);
        }
        return  (NULL);
    }
    
    static void *thread_b (void *arg)
    {
        while (1) {
            pthread_spin_lock(&lock);
            count++;
            pthread_spin_unlock(&lock);
            sleep(1);
        }
        return  (NULL);
    }
    
    int main (int argc, char *argv[])
    {
        pthread_t       threada_tid;
        pthread_t       threadb_tid;
        int             ret;
    
        ret = pthread_spin_init(&lock, FALSE);
        if (ret != 0) {
            fprintf(stderr, "mutex create failed.\n");
            return  (-1);
        }
        ret = pthread_create(&threada_tid, NULL, thread_a, NULL);
        if (ret != 0) {
            fprintf(stderr, "pthread create failed.\n");
            return  (-1);
        }
        ret = pthread_create(&threadb_tid, NULL, thread_b, NULL);
        if (ret != 0) {
            fprintf(stderr, "pthread create failed.\n");
            return  (-1);
        }
        pthread_join(threada_tid, NULL);
        pthread_join(threadb_tid, NULL);
        pthread_spin_destroy(&lock);
    
        return  (0);
    }
    

    在 SylixOS Shell 下运行程序:

    # ./POSIX_Spinlock
    thread_a(): count = 0
    thread_a(): count = 1
    thread_a(): count = 2
    thread_a(): count = 3
    thread_a(): count = 4
    thread_a(): count = 5
    thread_a(): count = 6
    ......
    # ts
              NAME     TID    PID  PRI STAT LOCK SAFE    DELAY   PAGEFAILS FPU CPU
    ---------------- ------- ----- --- ---- ---- ---- ---------- --------- --- ---
    POSIX_Spinlock   401008f    44 200 JOIN    0               0         1 USE   0
    pthread          4010090    44 200 SLP     0             626         0       0
    pthread          4010091    44 200 SLP     0             626         0       0
    
文档内容是否对您有所帮助?
有帮助
没帮助