Linux 自旋锁

目录

一、什么是自旋锁?

工作原理

优点与缺点

二、自旋锁的实现

三、内核API

pthread_spin_lock

pthread_spin_trylock

pthread_spin_unlock

pthread_spin_init

pthread_spin_destroy

四、自旋锁的适用场景


一、什么是自旋锁?

        自旋锁是一种多线程同步机制,用于保护共享资源免受并发访问的影响。它的核心思想是:当一个线程尝试获取锁时,如果锁已经被其他线程占用,它不会立即进入休眠状态,而是在一个循环中不断检查锁是否可用(即“自旋”)。这种方式避免了线程切换的开销,特别适合短时间内的锁竞争场景。

        与传统的互斥锁(Mutex)相比,自旋锁的最大区别在于等待策略:

  • 互斥锁:当获取锁失败时,线程主动让出CPU资源进入休眠状态

  • 自旋锁:采用忙等待(Busy-waiting)策略,线程持续检查锁状态


工作原理

自旋锁通常通过一个共享的标志位(如布尔值)来表示锁的状态:

  • 如果标志位为 false,表示锁可用,线程可以设置标志位为 true 并进入临界区。

  • 如果标志位为 true,表示锁已被占用,线程会不断循环检查标志位,直到锁被释放。

这种机制的核心在于使用原子操作(如 CAS 指令)来保证操作的线程安全性。

Compare-and-Swap(比较并交换)是实现自旋锁的关键原子操作:

// 伪代码示例
int compare_and_swap(int *ptr, int expected, int new_val) 
{
    int actual = *ptr;
    if (actual == expected) 
    {
        *ptr = new_val;
    }
    return actual;
}

当多个线程同时执行CAS操作时,硬件保证只有一个线程能成功修改值。这种机制也被称为乐观锁。


优点与缺点

优点

  • 低延迟:自旋锁不会让线程进入休眠状态,避免了线程切换的开销,特别适合短时间锁竞争场景。

  • 减少系统调度开销:等待锁的线程不会被阻塞,减少了上下文切换的频率。

缺点

  • CPU 资源浪费:如果锁的持有时间较长,等待线程会一直循环等待,导致 CPU 资源浪费。

  • 可能引起活锁:当多个线程同时自旋等待同一个锁时,如果没有适当的退避策略,可能导致所有线程都无法进入临界区。

二、自旋锁的实现

自旋锁的实现通常依赖于原子操作,常用的实现方式是通过 CAS(Compare-And-Swap)指令。以下是基于 C 语言的伪代码实现:

#include 
#include 
#include 
#include 

// 使用原子标志来模拟自旋锁
atomic_flag spinlock = ATOMIC_FLAG_INIT; // ATOMIC_FLAG_INIT 是 0

// 尝试获取锁
void spinlock_lock()
{
    while (atomic_flag_test_and_set(&spinlock))
    {
        // 如果锁被占用,则忙等待
    }
}

// 释放锁
void spinlock_unlock()
{
    atomic_flag_clear(&spinlock);
}

// atomic_flag 的底层结构
typedef _Atomic struct
{
#if __GCC_ATOMIC_TEST_AND_SET_TRUEVAL == 1
    _Bool __val;
#else
    unsigned char __val;
#endif
} atomic_flag;

说明:

  1. 原子性atomic_flag_test_and_set 是一个原子操作,确保在多线程环境中对标志位的读取和修改是不可分割的。

  2. 忙等待:如果锁被占用,线程会不断循环检查标志位,直到锁被释放。

三、内核API

#include 

// 获取自旋锁
int pthread_spin_lock(pthread_spinlock_t *lock);

// 尝试获取自旋锁(非阻塞)
int pthread_spin_trylock(pthread_spinlock_t *lock);

// 释放自旋锁
int pthread_spin_unlock(pthread_spinlock_t *lock);

// 初始化自旋锁
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

// 销毁自旋锁
int pthread_spin_destroy(pthread_spinlock_t *lock);

pthread_spin_lock

用途:获取自旋锁。如果锁已被其他线程占用,则线程会自旋等待,直到锁被释放。

函数原型

int pthread_spin_lock(pthread_spinlock_t *lock);

参数

  • lock:指向自旋锁对象的指针。

返回值

  • 成功:返回 0

  • 失败:返回错误码(如 EINVAL 表示参数无效)。


pthread_spin_trylock

用途:尝试获取自旋锁。如果锁已被占用,则立即返回,不会阻塞。

函数原型

int pthread_spin_trylock(pthread_spinlock_t *lock);

参数

  • lock:指向自旋锁对象的指针。

返回值

  • 成功:返回 0

  • 锁已被占用:返回 EBUSY

  • 失败:返回其他错误码。


pthread_spin_unlock

用途:释放自旋锁。

函数原型

int pthread_spin_unlock(pthread_spinlock_t *lock);

参数

  • lock:指向自旋锁对象的指针。

返回值

  • 成功:返回 0

  • 失败:返回错误码(如 EINVAL 表示参数无效)。


pthread_spin_init

用途:初始化自旋锁。

函数原型

int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

参数

  • lock:指向自旋锁对象的指针。

  • pshared:指定锁是否可以在不同进程间共享:

    • PTHREAD_PROCESS_PRIVATE:锁仅在当前进程内共享(默认值)。

    • PTHREAD_PROCESS_SHARED:锁可以在多个进程间共享。

返回值

  • 成功:返回 0

  • 失败:返回错误码(如 ENOMEM 表示内存不足)。


pthread_spin_destroy

用途:销毁自旋锁,释放相关资源。

函数原型

int pthread_spin_destroy(pthread_spinlock_t *lock);

参数

  • lock:指向自旋锁对象的指针。

返回值

  • 成功:返回 0

  • 失败:返回错误码(如 EINVAL 表示参数无效)。

四、自旋锁的适用场景

  • 短暂等待:适用于锁被占用时间很短的场景,如多线程对共享数据进行简单的读写操作。

  • 多线程锁使用:通常用于系统底层,同步多个 CPU 对共享资源的访问。

样例代码:售票系统

#include 
#include 
#include 
#include 
#include 

// 共享变量:剩余票数
int ticket = 1000;

// 自旋锁
pthread_spinlock_t lock;

// 线程函数:模拟售票
void *sell_ticket(void *arg)
{
    char *thread_id = (char *)arg;
    while (1)
    {
        // 尝试获取锁
        pthread_spin_lock(&lock);

        if (ticket > 0)
        {
            // 模拟售票操作
            usleep(1000);
            printf("%s sells ticket: %d\n", thread_id, ticket);
            ticket--;
        }
        else
        {
            // 如果票卖完了,释放锁并退出
            pthread_spin_unlock(&lock);
            break;
        }

        // 释放锁
        pthread_spin_unlock(&lock);
    }

    return NULL;
}

int main(void)
{
    // 初始化自旋锁
    pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE);

    // 创建多个线程模拟售票
    pthread_t t1, t2, t3, t4;
    pthread_create(&t1, NULL, sell_ticket, (void *)"Thread 1");
    pthread_create(&t2, NULL, sell_ticket, (void *)"Thread 2");
    pthread_create(&t3, NULL, sell_ticket, (void *)"Thread 3");
    pthread_create(&t4, NULL, sell_ticket, (void *)"Thread 4");

    // 等待所有线程结束
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);
    pthread_join(t4, NULL);

    // 销毁自旋锁
    pthread_spin_destroy(&lock);

    return 0;
}

Linux 自旋锁_第1张图片

你可能感兴趣的:(Linux,linux,自旋锁)