深入探究多线程并发中的同步机制:以 C 语言为例

在多线程编程中,对共享资源的并发访问是一个常见且棘手的问题。如果多个线程同时对共享资源进行读写操作,很容易出现数据竞争和不一致的情况,从而导致程序出现难以调试的错误。为了解决这个问题,我们通常会使用各种同步机制,如互斥锁、自旋锁和原子操作。本文将通过一个具体的 C 语言示例,详细介绍这些同步机制的使用和特点。

1.我们的示例代码旨在模拟多个线程同时对一个共享计数器进行递增操作的场景。通过使用不同的同步机制,我们可以观察到它们在性能和数据一致性方面的差异。以下是完整的代码

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

#define THREAD_COUNT 10

pthread_mutex_t mutex;
pthread_spinlock_t spinlock;

int inc(int *value,int add){
    int old;
    __asm__ volatile(
            "lock;xaddl %2,%1"
            :"=a"(old)
            :"m"(*value),"a"(add)
            :"cc","memory"
            );
}

void *thread_callback(void*arg){
    int *pcount=(int*)arg;
    int i =0;
    while (i++<100000){
#if 0
        (*pcount)++;
#elif 0
        //互斥锁 适用场景:锁的内容比较多,比如,线程安全的rbtree,添加可以用mutex
        pthread_mutex_lock(&mutex);
        (*pcount)++;
        pthread_mutex_unlock(&mutex);
#elif 0
        //自旋锁 适用场景:锁的内容少
        pthread_spin_lock(&spinlock);;
        (*pcount)++;
        pthread_spin_unlock(&spinlock);
#else
        //原子操作 单条CPU指令实现
        inc(pcount,1);
#endif
        usleep(1);
    }
}

int main(){
    pthread_t threadid[THREAD_COUNT] = {0};

    pthread_mutex_init(&mutex,NULL);
    pthread_spin_init(&spinlock,PTHREAD_PROCESS_SHARED);

    int i =0;
    int count=0;
    for( i =0;i<THREAD_COUNT;i++){
        pthread_create(&threadid[i],NULL, thread_callback,&count);
    }
    for (int i = 0; i <100; i++) {
        printf("count: %d\n",count);
        sleep(1);
    }
}

2.锁的定义

  • pthread_mutex_t:互斥锁类型,用于保护共享资源,确保同一时间只有一个线程可以访问。
  • pthread_spinlock_t:自旋锁类型,线程在等待锁时会一直忙等待,直到锁可用。

3.锁的实现

互斥锁:使用 pthread_mutex_lock 和 pthread_mutex_unlock 保护对 count 的访问,确保同一时间只有一个线程可以修改 count。互斥锁适用于锁的内容较多的场景,因为线程在等待锁时会被阻塞,不会消耗 CPU 资源。

自旋锁:使用 pthread_spin_lock 和 pthread_spin_unlock 保护对 count 的访问。自旋锁适用于锁的内容较少的场景,因为线程在等待锁时会一直忙等待,会消耗 CPU 资源,但避免了线程上下文切换的开销

4.原子操作

  • 该函数使用内联汇编实现了原子操作。lock;xaddl %2,%1 是一条 x86 汇编指令,lock 前缀确保该操作是原子的,xaddl 指令将 add 的值加到 *value 上,并返回 *value 的旧值。
  • 通过这种方式,我们可以在不使用锁的情况下实现对共享变量的原子递增操作
  • 调用 inc 函数实现原子递增,这种方式使用单条 CPU 指令完成操作,不需要额外的锁机制,性能较高。

5.锁的销毁

应该在程序结束前调用 pthread_mutex_destroy 和 pthread_spin_destroy 来释放锁资源。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值