0
点赞
收藏
分享

微信扫一扫

Linux C/C++ 开发(学习笔记四):多线程并发锁

爱我中华8898 2022-03-11 阅读 73

Linux C/C++ 开发(学习笔记四):多线程并发

一、多线程计数

在这里插入图片描述

背景:
火车抢票,总共10个窗口,每个窗口都同时进行10w张抢票
可以采用多线程的方式,火车票计数是公共的任务

#include<pthread.h>//posix线程 
#include<stdio.h>
#include<unistd.h>

#define THREAD_COUNT 10  //定义线程数10


//线程入口函数
void* thread_callback(void* arg){ 
    int* pcount=(int*)arg;
    int i=0;
    while(i++<100000){
        (*pcount)++;
        usleep(1);//单位微秒
    }
}

//10个窗口,同时对count进行++操作
int main(){

    pthread_t threadid[THREAD_COUNT]={0};//初始化线程id

    int count=0;
    for(int i=0;i<THREAD_COUNT;i++){//创建10个线程
        //第一个参数:返回线程。 第二个参数:线程的属性(堆栈)。第三个:线程的入口函数。第四个:主线程往子线程传的参数
        pthread_create(&threadid[i],NULL,thread_callback,&count);//count是传入thread_callback内的
    }

    for(int i=0;i<100;i++){
        printf("count: %d\n",count);
        sleep(1);//单位秒
    }
}


虽然包含了线程的头文件<pthread.h>,可是编译的时候却报错“对pthread_create未定义的引用“,原来时因为 pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数

可以使用下面方法编译

g++ thread_count.cpp -o thread_count -lpthread

执行后,发现,按理说要执行到100w,可是停到99w多就结束了。
在这里插入图片描述

二、发现问题

理想状态,线程应该是这样的
在这里插入图片描述
但实际上存在,执行完线程1MOV操作后,线程1切换到线程2。导致两个线程的操作,本应该50->52,但是结果确实50->51
在这里插入图片描述

count是一个临界资源(两个线程共享一个变量),因此为了避免上述这种情况发生,要加锁

三、互斥锁

当一个线程在执行一个指令的时候,另一个线程进不来。
相当于把count++转化为汇编的3行命令给打包在一起。

定义互斥锁

pthread_mutex_t mutex;//定义互斥锁

初始化互斥锁

pthread_mutex_init(&mutex,NULL);//互斥锁初始化(第二个参数是 锁的属性)

加锁/解锁

//加了互斥锁
 pthread_mutex_lock(&mutex);
 (*pcount)++;
 pthread_mutex_unlock(&mutex);

完整代码

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

#define THREAD_COUNT 10

pthread_mutex_t mutex;//定义互斥锁

void* thread_callback(void* arg){
    int* pcount=(int*)arg;
    int i=0;
    while(i++<100000){
#if 0
        (*pcount)++;
#else   
        //加了互斥锁
        pthread_mutex_lock(&mutex);
        (*pcount)++;
        pthread_mutex_unlock(&mutex);
#endif
        usleep(1);//单位微秒

    }
}

//10个窗口,同时对count进行++操作
int main(){

    pthread_t threadid[THREAD_COUNT]={0};//初始化线程id
    pthread_mutex_init(&mutex,NULL);//互斥锁初始化(第二个参数是 锁的属性)

    int count=0;
    for(int i=0;i<THREAD_COUNT;i++){//创建10个线程
        //第一个参数:返回线程。 第二个参数:线程的属性(堆栈)。第三个:线程的入口函数。第四个:主线程往子线程传的参数
        pthread_create(&threadid[i],NULL,thread_callback,&count);//count是传入thread_callback内的
    }

    for(int i=0;i<100;i++){
        printf("count: %d\n",count);
        sleep(1);//单位秒
    }
}

四、自旋锁

在写法上和互斥锁基本上没有差别

定义自旋锁

pthread_spinlock_t spinlock;//定义自旋锁

初始化自旋锁

pthread_spin_init(&spinlock,PTHREAD_PROCESS_SHARED);//自旋锁初始化(第二个参数是 进程共享)

自旋锁(加锁/解锁)

//加了自旋锁
pthread_spin_lock(&spinlock);
(*pcount)++;
pthread_spin_unlock(&spinlock);

完整代码

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

#define THREAD_COUNT 10
// pthread_mutex_t mutex;
pthread_spinlock_t spinlock;//定义自旋锁
void* thread_callback(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);
#else 
        //加了自旋锁
        pthread_spin_lock(&spinlock);
        (*pcount)++;
        pthread_spin_unlock(&spinlock);
#endif
        usleep(1);

    }
}

int main(){

    pthread_t threadid[THREAD_COUNT]={0};
    // pthread_mutex_init(&mutex,NULL);
    pthread_spin_init(&spinlock,PTHREAD_PROCESS_SHARED);//自旋锁初始化(第二个参数是 进程共享)
    int count=0;
    for(int 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);//单位秒
    }
}

五、互斥锁和自旋锁的对比

自选锁的介绍
在这里插入图片描述

当锁的内容很少的时候,继续等待的时间代价比 线程切换的时间代价更小的 时候,选择使用自旋锁。
锁的内容比较多的时候,使用互斥锁。(比如,线程安全的红黑树,可以使用mutex)

也就是说:
锁的内容少-》用自旋锁
锁的内容多-》用互斥锁

六、原子操作

如果把这三条指令变成一条,那么就不会出现,这种问题了。
在这里插入图片描述
原子操作:单条cpu指令实现

在这里插入图片描述

完整代码

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

#define THREAD_COUNT 10
// pthread_mutex_t mutex;
// pthread_spinlock_t spinlock;


//用一个函数去实现
int increase(int *value,int add){
    int old;
    __asm__ volatile(
        "lock;xaddl %2,%1;"
        :"=a"(old)
        :"m"(*value),"a"(add)
        :"cc","memory"
    );

    return old;
}

void* thread_callback(void* arg){
    int* pcount=(int*)arg;
    int i=0;
    while(i++<100000000){
#if 0
        (*pcount)++;
#elif 0 
        //加了互斥锁
        pthread_mutex_lock(&mutex);
        (*pcount)++;
        pthread_mutex_unlock(&mutex);
#elif 0 
        //加了自旋锁
        pthread_spin_lock(&spinlock);
        (*pcount)++;
        pthread_spin_unlock(&spinlock);
#else
		//原子操作
        increase(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 count=0;
    for(int 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);//单位秒
    }
}

七、其他:CAS

CAS

举报

相关推荐

0 条评论