锁和原子操作CAS的底层实现

本文深入探讨了多线程环境下资源竞争问题,介绍了互斥锁、自旋锁、读写锁以及原子操作的概念和用法。通过示例代码展示了在并发编程中如何防止数据不一致,确保线程安全。同时,讨论了不同锁的优缺点,如自旋锁避免了线程切换开销,但在某些场景下可能导致资源浪费。
摘要由CSDN通过智能技术生成

锁的由来

在多线程或多进程下,有些变量需要共享使用就会产生资源竞争,这个资源称为临界资源

互斥锁

自旋锁

原子操作

本质多条指令,合成一个执行
理想情况下多个线程执行 idx++,每个线程都能完整执行完Idx++,再切换
在这里插入图片描述
可能出现在线程和CPU切换时,线程1的idx已经拷贝到寄存器中,还未同步++,再从寄存器读出来就发生线程切换,这样线程2取的值依然是原本的
最后导致,两个线程只++了一次
在这里插入图片描述

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#define THREAD_SIZE     10

int count = 0;
pthread_mutex_t mutex;//互斥锁
pthread_spinlock_t spinlock;//自旋锁
pthread_rwlock_t rwlock;//读写锁

// MOV dest, src;  at&t Linux
// MOV src, dest;  x86 Windows 汇编器实现语法不一样 
// 原子操作 
int inc(int *value, int add) {
    int old;
    //不定义的时候 优先寄存器的值 编译器会优化 加载到寄存器 改变再写回内存
	//volatile  易变的 直接操作内存的值 CPU计算时候会直接取它
	//内存屏障 汇编方式
    __asm__ volatile ( 
        "lock; xaddl %2, %1;" // "lock; xchg %2, %1, %3;" 
        : "=a" (old)
        : "m" (*value), "a" (add)
        : "cc", "memory"
    );

    return old;
}


// 
void *func(void *arg) {

    int *pcount = (int *)arg;

    int i = 0;
    while (i++ < 100000) {
#if 0
        (*pcount) ++;//不加锁 自增
#elif 0
		//使用互斥锁 
        pthread_mutex_lock(&mutex);
        (*pcount) ++;
        pthread_mutex_unlock(&mutex);

#elif 0
        //尝试加锁 不休眠 但是运行消耗大,且可能线程切换 反应更快 成功返回0 
        if (0 != pthread_mutex_trylock(&mutex)) {
            i --;
            continue;
        }
        (*pcount) ++;
        pthread_mutex_unlock(&mutex);
#elif 1
		//自旋锁 不会发生线程切换 且不会休眠 自动循环尝试加锁
        pthread_spin_lock(&spinlock);
        (*pcount) ++;
        pthread_spin_unlock(&spinlock);
#elif 0
		//读写锁 多线程 只允许一个线程写 允许多个线程读
        pthread_rwlock_wrlock(&rwlock);
        (*pcount) ++;
        pthread_rwlock_unlock(&rwlock);

#else

        inc(pcount, 1);

#endif
        usleep(1);
    }

}


int main() {
#if 1
	//10个线程 对一个count变量 ++ ,每个线程执行10w次 理论 count最终等于100W 但实际不加锁情况是<100W
    pthread_t threadid[THREAD_SIZE] = {0};

    pthread_mutex_init(&mutex, NULL);//互斥锁初始化
    pthread_spin_init(&spinlock, PTHREAD_PROCESS_SHARED);//自旋锁初始化
    pthread_rwlock_init(&rwlock, NULL);//读写锁初始化

    int i = 0;
    int count = 0;
    for (i = 0;i < THREAD_SIZE;i ++) {
        int ret = pthread_create(&threadid[i], NULL, func, &count);
        if (ret) {
            break;
        }
    }
	
    for (i = 0;i < 100;i ++) {
        //pthread_rwlock_rdlock(&rwlock);
        printf("count --> %d\n", count);
        //pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
#else
	//多进程下 加锁
	//共享内存 
	//1.fd NULL 2.分配int大小 3.可读可写 4.以xx方式映射 5.-1 6.0
	// 这个适合父子进程 不适合文件打开
    int *pcount = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
    int i = 0;
    pid_t pid = 0;
    for (i = 0;i < THREAD_SIZE;i ++) {
		//循环创建多个进程 
        pid = fork();
        if (pid <= 0) {
        	//这里 pid=0 是子进程,就不要fork了
            usleep(1);//fork完后 让子进程先行
            break;
        }
    }
    //父进程就是主进程负责打印
    if (pid > 0) { 
    	//执行打印 
        for (i = 0;i < 100;i ++) {
            printf("count --> %d\n",  (*pcount));
            sleep(1);
        }
    } else {
    	//其他10个进程负责
        int i = 0;
        while (i++ < 100000)  {
#if 1            
            (*pcount) ++;
#else
            inc(pcount, 1);
#endif
            usleep(1);
        }
    }
#endif 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值