互斥锁与自旋锁

下面摘录一些关于C++当中
自旋锁(spin lock)与互斥量(mutex)的比较
自旋锁是一种非阻塞锁,也就是说,如果某线程需要获取自旋锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取自旋锁。
互斥量是阻塞锁,当某线程无法获取互斥量时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活那个被挂起的线程,让其投入运行。

两种锁适用于不同场景:
如果是多核处理器,如果预计线程等待锁的时间很短,短到比线程两次上下文切换时间要少的情况下,使用自旋锁是划算的。
如果是多核处理器,如果预计线程等待锁的时间较长,至少比两次线程上下文切换的时间要长,建议使用互斥量。
如果是单核处理器,一般建议不要使用自旋锁。因为,在同一时间只有一个线程是处在运行状态,那如果运行线程发现无法获取锁,只能等待解锁,但因为自身不挂起,所以那个获取到锁的线程没有办法进入运行状态,只能等到运行线程把操作系统分给它的时间片用完,才能有机会被调度。这种情况下使用自旋锁的代价很高。
如果加锁的代码经常被调用,但竞争情况很少发生时,应该优先考虑使用自旋锁,自旋锁的开销比较小,互斥量的开销较大。

代码测试:
互斥锁:

#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<errno.h>
#include<pthread.h>
#include<sys/time.h>

int num = 0;
int count = 100000;
pthread_mutex_t mulock;

long long getSystemTime(){
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return 1000 * 1000 * tv.tv_sec + tv.tv_usec;
}

void* testmutex(void* arg){
    (void)arg;
    pthread_t thread_id = pthread_self();
    printf("this thread id %ld\n", thread_id);
    for(int i = 0 ; i < count ; ++i){
        pthread_mutex_lock(&mulock);
        num++;
        pthread_mutex_unlock(&mulock);
    }
}

int main()
{
    pthread_t tid1;
    pthread_t tid2;
    tid1 = pthread_self();
    printf("this thread is %ld\n", tid1);

    long long Start_time = getSystemTime();
    pthread_create(&tid2, NULL, testmutex, NULL);
    for(int i = 0; i < count; ++i){
        pthread_mutex_lock(&mulock);
        num++;
        pthread_mutex_unlock(&mulock);
    }
    pthread_join(tid2, NULL);

    long long End_time = getSystemTime();
    printf("use time is %lld, num is %d\n", End_time - Start_time, num );
    pthread_mutex_destroy(&mulock);
    return 0;
}

自旋锁:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/time.h>
#include<stdlib.h>
#include<errno.h>

int num = 0;
int count = 100000;
pthread_spinlock_t splock;

long long getSystemTime(){
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return 1000* 1000 *tv.tv_sec + tv.tv_usec;
}

void* testSpin(void* arg){
    (void)arg;
    pthread_t thread_id = pthread_self();
    printf("this thread is %ld\n", thread_id);
    int i = 0;
    for(; i < count; ++i){
        pthread_spin_lock(&splock);
        num++;
        pthread_spin_unlock(&splock);
    }
}

int main()
{
    pthread_t tid1;
    pthread_t tid2;
    pthread_spin_init(&splock,PTHREAD_PROCESS_PRIVATE ); 
    tid1 = pthread_self();
    printf("this thread is %ld\n", tid1);

    long long Start_time = getSystemTime();

    int ret = pthread_create(&tid2, NULL, testSpin, NULL);
    if(ret != 0){
        perror("pthread_create");
        return -1;
    }
    for(int i = 0 ; i < count; ++i){
        pthread_spin_lock(&splock);
        num++;
        pthread_spin_unlock(&splock);
    }

    pthread_join(tid2, NULL);
    long long End_time = getSystemTime();
    printf("use time %lld, num = %d\n", End_time - Start_time , num);
    pthread_spin_destroy(&splock);

    return 0;
}

结果:

这里写图片描述

### 互斥锁自旋锁和信号量的概念及使用场景 #### 概念定义 - **互斥锁 (Mutex)** 是一种用于保护共享资源的机制,确保同一时刻只有一个线程能够访问该资源。它通过加锁和解锁的操作来控制对临界区的访问[^3]。 - **自旋锁 (Spin Lock)** 是一种轻量级的锁,在尝试获取锁的过程中会不断循环检查直到锁可用为止。这种行为称为“忙等待”,适用于短时间持有锁的情况[^1]。 - **信号量 (Semaphore)** 提供了一种更灵活的方式管理多个资源的访问权限。它可以表示一组资源的数量,并支持计数功能。信号量分为二进制信号量(类似于互斥锁)和计数信号量两种类型[^3]。 --- #### 使用场景分析 - **互斥锁** - 主要应用于单处理器或多处理器环境中,当某个线程需要长时间占用某一资源时,适合采用互斥锁[^3]。 - 场景实例:文件系统的写入操作,数据库事务处理等需要严格串行化的场合。 - **自旋锁** - 更适合于多核环境下短期锁定需求,因为其不会让出 CPU 时间片而是持续轮询锁的状态[^4]。 - 如果预计锁会被很快释放,则自旋锁是一种高效的选择;但如果锁持有时过长则会造成不必要的 CPU 资源浪费[^1]。 - 应用案例:中断处理程序之间的小范围同步问题。 - **信号量** - 当存在多个相同类型的资源共享情况时非常有用,比如生产者消费者模型中缓冲队列大小限制可以通过信号量有效解决。 - 它还解决了某些特定条件下可能出现的竞争状况,例如优先级反转等问题可通过高级特性规避[^2]。 --- #### 区别对比表 | 特性 | 互斥锁 | 自旋锁 | 信号量 | |-----------------|----------------------------------|-----------------------------------|-------------------------------------| | 是否阻塞 | 阻塞 | 不阻塞(忙等待) | 可配置为阻塞或者非阻塞 | | 开销 | 较高 | 较低 | 中等 | | 同步粒度 | 单一线程 | 多个CPU核心 | 支持多个资源 | | 死锁风险 | 存在 | 存在 | 易受错误调用序列影响 | --- #### 联系总结 尽管三者的具体实现和技术细节有所不同,但它们都旨在解决并发编程中的同步互斥问题。选择哪种工具取决于实际应用场景的要求以及性能考量因素: - 对于较长期限内的独占式访问建议选用 Mutex; - 若预期等待时间极短且运行环境具备足够的计算能力可考虑 SpinLock; - Semaphore 则更适合涉及数量变化或需协调多方协作的任务情境下运用[^1]^. ```c // 示例代码展示如何初始化并使用这些同步原语 #include <pthread.h> #include <semaphore.h> pthread_mutex_t mutex; spinlock_t spin_lock; sem_t semaphore; void init_sync_primitives() { pthread_mutex_init(&mutex, NULL); // 初始化互斥锁 spin_lock_init(&spin_lock); // 初始化自旋锁 [^4] sem_init(&semaphore, 0, 1); // 初始化信号量 } void lock_and_unlock_example() { pthread_mutex_lock(&mutex); /* Critical Section */ pthread_mutex_unlock(&mutex); spin_lock(&spin_lock); /* Critical Section */ spin_unlock(&spin_lock); sem_wait(&semaphore); /* Critical Section */ sem_post(&semaphore); } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值