POSIX 自旋锁

更新时间:
2024-03-14
下载文档

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 自旋锁的指针。
  1. 自旋锁的加锁与解锁
#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
文档内容是否对您有所帮助?
有帮助
没帮助