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\n", id);
    printf("thread main %d get the lock end\n", 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\n", 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)通过休眠阻塞进程/线程,确保同一时间只有一个线程访问数据。休眠,也就意味着会放弃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\n", id);
    printf("thread main %d get the lock end\n", 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;
}

Linux 自旋锁与互斥量(互斥锁)的区别

上一篇:移动端布局


下一篇:利用ICMP隧道反弹shell