Linux 线程同步(互斥锁、读写锁、死锁、条件变量、信号量)

1、互斥锁(互斥量)

线程同步:多个线程同时访问共享数据时,让他有先后顺序,多个线程争抢锁,抢到锁的可以对共享数据进行操作,没有抢到的线程堵塞等待,等待操作的线程解锁

操作函数

pthread_mutex_t     // 创建锁
pthread_mutex_init  // 初始化
pthread_mutex_lock // 加锁
pthread_mutex_unlock // 解锁
pthread_mutex_destroy //销毁锁
都是成功返回0,失败返回-1

操作流程:
1、创建一把互斥锁
2、初始化互斥锁
3、加锁
4、解锁
5、销毁锁

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

pthread_mutex_t mutex; //   定义一把互斥锁 (是一个整数)

void* fun(void* arg)
{
    srand(time(0));
    while(1)
    {
        pthread_mutex_lock(&mutex); // 加锁 锁--
        printf("hello ");
        sleep(rand()%3);    
        printf("world\n");
        
        pthread_mutex_unlock(&mutex);   // 解锁 锁++
        sleep(rand()%3);

    }
    return NULL;
}

int main()
{
    pthread_t tid;
    srand(time(NULL));
    int ret = pthread_mutex_init(&mutex,NULL);  // 锁变成 1
    if(ret != 0)
    {
        fprintf(stderr,"pthread_mutex_init error:%s\n",strerror(ret));
        exit(1);
    }

    pthread_create(&tid,NULL,fun,NULL);
    while(1)
    {
        pthread_mutex_lock(&mutex); // 加锁
        printf("Hello ");
        sleep(rand()%3);    
        printf("World\n");
        
        pthread_mutex_unlock(&mutex);   // 解锁

        sleep(rand()%3);
    }

    ret = pthread_join(tid,NULL);
    if(ret != 0)
    {
        fprintf(stderr,"pthread_join error:%s\n",strerror(ret));
        exit(1);
    }
    pthread_mutex_destroy(&mutex);  // 销毁互斥锁

    pthread_exit((void*)0);
}

2、读写锁

1、写独占,读共享(同时来写的优先级高,优先给写锁)
2、只有一把锁
3、 相较于互斥锁(量)而言,当读线程多的时候,提高效率

读写锁函数

pthread_rwlock_t rwlock	读写锁
pthread_rwlock_init	初始化
pthread_rwlock_rdlock	读锁
pthread_rwlock_wrlock	写锁
pthread_rwlock_unlock	解锁
pthread_rwlock_destroy	销毁锁

函数都没有检查返回值,为了突出逻辑,都是成功返回0,失败返回-1

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


pthread_rwlock_t rwlock;    // 创建一把读写锁
int var = 100;  // 共享数据

void* fun1(void* arg)   
{
    while (1)
    {
        pthread_rwlock_rdlock(&rwlock);     // 添加读锁
        printf("var=%d\n",var);
        pthread_rwlock_unlock(&rwlock); // 解锁
        sleep(rand()%1);
    }
    
    return NULL;
}

void* fun2(void* arg)
{
    srand(time(NULL));  // 随机数种子
    while (1)
    {
        pthread_rwlock_wrlock(&rwlock); // 添加写锁
        var = rand()%10000+1;
        printf("写锁将数据修改成:%d\n",var);
        pthread_rwlock_unlock(&rwlock);  // 解锁

        sleep(rand()%1);    // 预留一下时间,让其他线程争抢到锁
    }
    
    return NULL;
}

int main()
{
    pthread_t tid1,tid2,tid3,tid4;
    pthread_rwlock_init(&rwlock,NULL);    // 初始化读写锁
    pthread_create(&tid1,NULL,fun1,NULL);   // 创建线程
    pthread_create(&tid2,NULL,fun2,NULL);

    pthread_create(&tid3,NULL,fun1,NULL);   // 多创建两个读线程
    pthread_create(&tid4,NULL,fun1,NULL);

    pthread_join(tid1,NULL);    // 回收线程
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    pthread_rwlock_destroy(&rwlock); // 销毁读写锁

    pthread_exit((void*)0);
}

3、死锁

在这里插入图片描述

是使用锁不恰当导致的现象:
1、对一个锁反复lock当前这个线程,已经进行了一次加锁,后面又调用了一次加锁,因为加锁失败,就会堵塞,在这里等,就会没办法解锁了
2、两个线程,各自持有一把锁,请求另一把

4、条件变量

条件变量函数

本身不是锁,但是通常结合锁来使用,mutex
pthread_cond_t cond; 条件变量
pthread_cond_init 初始化
pthread_cond_destroy函数
pthread_cond_wait	等待
pthread_cond_signal();   唤醒阻塞在条件变量上的(至少)一个线程
pthread_cond_broadcast();   唤醒阻塞在条件变量上的所有线程

在这里插入图片描述

pthread_cond_wait函数比较复杂:
1、阻塞等待一个条件变量(需要等待pthread_cond_signal函数唤醒)
2、释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
3、当被唤醒,进行加锁
这个函数总共做了三件事

在这里插入图片描述

两个线程:
1、模拟消费者
	先判断链表中有没有数据,要是没有等待(pthread_cond_wait),等待生产者生产数据,并唤醒我,然后消耗数据,并解锁
	
2、模拟生产者
	先生产一个数据,拿锁(因为要访问共享数据就需要拿锁),将数据挂到链表上(头插法),解锁,然后通知消费者,有数据了
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

struct msg
{
    struct msg* next;
    int num;
};

struct msg* head;

// 静态初始化 一个条件变量,和一个互斥量
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void* consumer(void* arg)
{
    struct msg* mp;
    for(;;)
    {
        pthread_mutex_lock(&lock);
        while(head == NULL) // 头指针为空,说明没有节点
        {
            pthread_cond_wait(&has_product,&lock);  
        }
        mp = head;
        head = mp->next;    // 模拟消费掉一个商品
        pthread_mutex_unlock(&lock);   
        
        printf("consumer:%lu--%d\n",pthread_self(),mp->num);
        free(mp);
        sleep(rand()%3);

    }
}

void* producer(void* arg)
{  
    struct msg* mp;
    for(;;)
    {
        mp = malloc(sizeof(struct msg));
        mp->num = rand()%1000+1;    // 模拟生产一个产品
        printf("produce ------%d\n",mp->num);
        pthread_mutex_lock(&lock);
        mp->next = head;
        head = mp;
        pthread_mutex_unlock(&lock);

        pthread_cond_signal(&has_product);  // 将等待在该条件变量上的一个线程唤醒
        sleep(rand()%3);
    }
}

int main()
{
    pthread_t cid,pid,new;
    srand(time(NULL));

    pthread_create(&pid,NULL,producer,NULL);
    pthread_create(&cid,NULL,consumer,NULL);
    // pthread_create(&new,NULL,consumer,NULL);
    
    pthread_join(cid,NULL);
    pthread_join(pid,NULL);
    // pthread_join(new,NULL);

    pthread_exit((void*)0);
}

5、信号量

相当于 初始化值为N 的互斥量;(相当于可以同时有N个可以访问)

信号量函数

sem_t sem;  定义类型
int sem_init(sem_t *sem,int pshared,unsigned int value);
参数:
    sem:信号量
    pshared: 0 用于线程间同步
              1 用于进程间同步
    value   N指定同时访问的线程数   

sem_init();
sem_destroy();
sem_wait();一次调用,做一次--操作,当信号的值为0时,再次--就会阻塞(对比pthread_mutex_lock)
sem_post();一次调用,做一次++操作,当信号量的值,N时,再次++就会阻塞,(对比pthread_mutex_unlock)

在这里插入图片描述

初始化是相对于有5个格子可以装东西
现在东西的数量是0个
两者有一者为0时,就会堵塞

1、生产者
	东西数量进行++,格子数量进行--
	
2、消费者
	东西数据--,格子数进行++
	
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>

#define NUM 5
int queque[NUM];    // 全局数组实现环形队列

sem_t blank_number,product_number;  // 空格子信号量,产品信号量

void* producer(void* arg)
{
    int i=0;
    while (1)
    {
        sem_wait(&blank_number);    // 生产者将格子数--,为0则阻塞等待
        queque[i] = rand()%1000+1;  // 生产一个产品
        printf("produce--%d\n",queque[i]);
        sem_post(&product_number);  // 将产品数++
        i = (i+1)%NUM; // 借助下标实现环形
        sleep(rand()%1);
    }
    
}

void* comsumer(void* arg)
{
    
    int i=0;
    while(1)
    {   
        sem_wait(&product_number);  // 产品数--,为0则阻塞等待
        printf("consumer--%d\n",queque[i]);
        queque[i] = 0;
        sem_post(&blank_number);    // 消费以后,将空格子数++
        i = (i+1)%NUM;
        sleep(rand()%3);

    }
}


int main()
{   
    sem_init(&blank_number,0,NUM);  // 初始化空格子信号量是5
    sem_init(&product_number,0,0);  // 产品初始化是0

    pthread_t pid,cid;
    pthread_create(&pid,NULL,producer,NULL);
    pthread_create(&cid,NULL,comsumer,NULL);

    pthread_join(cid,NULL);
    pthread_join(pid,NULL);
    
    sem_destroy(&blank_number);
    sem_destroy(&product_number);

    pthread_exit((void*)0);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讳疾忌医丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值