首页 > 系统相关 >C语言——linux 【互斥锁、死锁、信号量、条件变量】内附代码及练习

C语言——linux 【互斥锁、死锁、信号量、条件变量】内附代码及练习

时间:2025-01-16 11:29:28浏览次数:3  
标签:int 内附 互斥 死锁 train cond pthread mutex include

1、思维导图

2、互斥锁

1.互斥锁实现互斥的代码

3.防死锁

默认防死锁 trylock(不推荐,容易破环互斥的同步性)

常用防死锁的方式有——递归锁、错误检查锁

函数原型:int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
功能描述:将互斥锁属性attr,设置成kind类型
参数描述:
        参数 attr:互斥锁属性地址
        参数 kind:互斥锁到底是默认的快速锁,还是递归锁,还是错误检查锁,全都取决于这个kind
            PTHREAD_MUTEX_FAST_NP:快速锁
            PTHREAD_MUTEX_RECURSIVE_NP:递归锁
            PTHREAD_MUTEX_ERRORCHECK_NP:错误检查锁

3.1 默认的普通互斥锁:trylock

函数原型:int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能描述:尝试上锁互斥锁,如果该互斥锁已经上锁,则会上锁失败。无论互斥锁被任意线                     程上锁,trylock都不会阻塞

返回值:成功上锁返回0,失败上锁返回错误码
所以trylock虽然能够解决死锁问题,但是trylock容易破坏互斥和同步性

3.2递归锁互斥代码

递归锁的行为模式:

    当一把递归锁已经上锁的状态下,再次去上锁的话,lock函数会立刻返回,

并且记录该线程上锁了几次这个递归锁

如果该线程想要解锁的话,就必须解锁对应次数的递归锁

lock一把已经上锁的递归锁能够不阻塞并立刻返回的前提条件是:

①多次lock行为必须发生在同一个线程

②不同线程的lock行为依旧会阻塞

静态创建递归锁

pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

动态创建递归锁

第一步:创建互斥锁
            pthread_mutex_t m;
第二步:创建互斥锁属性
            pthread_mutexattr_t attr;
第三步:初始化互斥锁属性
    函数原型:int pthread_mutexattr_init(pthread_mutexattr_t *attr);
    功能描述:初始化互斥锁的属性
    返回值:总是返回0
    调用形式:
                pthread_mutexattr_init(&attr)
第四步:将互斥锁的属性,设置为递归锁
    函数原型:int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
    功能描述:将互斥锁属性attr,设置成kind类型
    参数描述:
        参数 attr:互斥锁属性地址
        参数 kind:锁类型 如下3个
            PTHREAD_MUTEX_FAST_NP:快速锁
            PTHREAD_MUTEX_RECURSIVE_NP:递归锁
            PTHREAD_MUTEX_ERRORCHECK_NP:错误检查锁
    调用形式:
        pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP)

3.3 错误检查锁互斥代码

错误检查锁的行为模式:

当一把已经上锁的互斥锁再次上锁的时候,lock函数立刻返回错误码

错误检查锁和递归锁一样的,仅当同一线程反复lock错误检查锁的情况下,多出来的lock才会立刻返回错误码   其他线程的lock正常阻塞

静态创建错误检查锁:

pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;

动态创建步骤和 递归锁   前3步  一致仅第4步 锁类型 属性设置不同

        函数原型:int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);

        参数 attr:互斥锁属性地址
        参数 kind:锁类型 如下3个
            PTHREAD_MUTEX_FAST_NP:快速锁
            PTHREAD_MUTEX_RECURSIVE_NP:递归锁
            PTHREAD_MUTEX_ERRORCHECK_NP:错误检查锁

 pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK_NP)

 4.信号量

4.1信号量互斥代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>

typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;

int TV_channel = 1;
sem_t s;


void task1(){
	// 分支线程
	while(1){
		sem_wait(&s); // 获取遥控器
		TV_channel = 5;
		sem_post(&s); // 释放遥控器
	}
}

void task2(){
	// 主线程
	while(1){
		sem_wait(&s); // 获取遥控器
		TV_channel = 100;
		usleep(10);
		printf("电视频道 = %d\n",TV_channel);// 电视看完了
		sem_post(&s); // 获取遥控器
	}
}

void* thread_main(void* arg){
	task1();
}

int main(int argc, const char *argv[])
{
	sem_init(&s,0,1);

	pthread_t id;
	pthread_create(&id,0,thread_main,0);
	pthread_detach(id);
	task2();
	return 0;
}

4.2信号量同步代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>

typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;

sem_t s0;
sem_t s1;
sem_t s2;


void task_main(){
	while(1){
		sem_wait(&s0);//尝试获取遥控器
		printf("主线程看电视\n");// 到这里的时候s0 已经没有遥控器了
		sleep(1);
		sem_post(&s1);// 给 s1 增加一个遥控器
	}
}

void task1(){
	while(1){
		sem_wait(&s1);//尝试获取遥控器
		printf("1#线程看电视\n");
		sleep(1);
		sem_post(&s2);
	}
}

void task2(){
	while(1){
		sem_wait(&s2);//尝试获取遥控器
		printf("2#线程看电视\n");
		sleep(1);
		sem_post(&s0);
	}
}


void* thread_main_1(void* arg){
	task1();
}

void* thread_main_2(void* arg){
	task2();
}

int main(int argc, const char *argv[])
{
	sem_init(&s0,0,1);
	sem_init(&s1,0,0);
	sem_init(&s2,0,0);

	pthread_t id;
	pthread_create(&id,0,thread_main_1,0);
	pthread_detach(id);
	pthread_t id2;
	pthread_create(&id2,0,thread_main_2,0);
	pthread_detach(id2);
	task_main();
	return 0;
}

5.条件变量

5.1 创建条件变量和互斥锁变量

pthread_mutex_t mutex;

pthread_cond_t cond;

5.2 条件变量初始化

静态初始化       

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

动态初始化
函数原型:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
功能描述:初始化一个条件变量
参数描述:
    参数 cond:条件变量的地址
    参数 attr:条件变量属性      这里只能写 NULL
返回值:成功返回0,失败返回错误码
调用形式:
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    pthread_cond_init(&cond,NULL);

5.3询问运行条件是否满足 

函数原型:int pthread_cond_wait  (pthread_cond_t *cond, pthread_mutex_t *mutex);
功能描述:询问运行条件是否满足
注意事项:pthread_cond_wait 这个函数会做3件事情
    1:解锁mutex互斥锁
    2:等待回复条件满足
    3:重新运行当前线程之前,会重新上锁mutex互斥锁
参数描述:
    参数 cond: 准备等待的条件变量地址
    参数 mutex:想要解锁的互斥锁的地址
返回值:成功返回0,失败返回错误码

调用形式:
    pthread_cond_wait(&cond,&mutex)  

5.4  唤醒wait函数的等待

函数原型:int pthread_cond_signal  (pthread_cond_t *cond);
功能描述:唤醒一个 pthread_cond_wait 函数的等待
注意事项:只会唤醒一个被wait的线程,如果没有线程处于wait当中,什么都不发生。

                 如果多个线程同时处于wait,则只会唤醒一个,但是不确定具体是哪个.


当signal函数被调用时,所有处于静止态的被wait的线程,同时进入就绪态,去抢夺时间片

抢到时间片的线程就被唤醒开始运行,没抢到时间片的剩余线程,则重新回到静止态

参数描述:
    参数 cond:准备唤醒的条件地址
返回值:成功返回0,失败返回错误码

调用形式:
    pthread_cond_signal(&cond)

函数原型:int pthread_cond_broadcast  (pthread_cond_t *cond);
功能描述:唤醒所有被wait阻塞的线程
注意实现:总是先唤醒先被wait阻塞的线程
参数描述:
    参数 cond:准备唤醒的条件地址
返回值:成功返回0,失败返回错误码

调用形式:
    pthread_cond_broadcast (&cond)   

 

 5.5 销毁条件变量

函数原型:int pthread_cond_destroy  (pthread_cond_t *cond);
功能描述:销毁条件变量
注意事项:销毁条件变量之前,不能有任何线程被wait阻塞
参数描述:
    参数 cond:准备唤醒的条件地址
返回值:成功返回0,失败返回错误码

调用形式:
    pthread_cond_destroy  (&cond)   

 5.6 条件变量实现互斥

 5.7 条件变量实现同步

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>

typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;

pthread_mutex_t mutex;
pthread_cond_t cond1;
pthread_cond_t cond2;
pthread_cond_t cond3;

void* task1(void* arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond1,&mutex);
		printf("线程1唤醒\n");
		pthread_mutex_unlock(&mutex);
	}
}

void* task2(void* arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond2,&mutex);
		printf("线程2唤醒\n");
		pthread_mutex_unlock(&mutex);
	}
}

void* task3(void* arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond3,&mutex);
		printf("线程3唤醒\n");
		pthread_mutex_unlock(&mutex);
	}
}

int main(int argc, const char *argv[])
{
	pthread_t id1,id2,id3;
	pthread_create(&id1,0,task1,0);
	pthread_create(&id2,0,task2,0);
	pthread_create(&id3,0,task3,0);
	pthread_detach(id1);
	pthread_detach(id2);
	pthread_detach(id3);

	int ch = 0;
	while(1)
	{
		printf("\n");
		printf("1:唤醒1#线程\n");
		printf("2:唤醒2#线程\n");
		printf("3:唤醒3#线程\n");
		printf("4:唤醒所有线程\n");
		printf("0:退出线程\n");
		printf("请输入:");
		scanf("%d",&ch);
		while(getchar()!=10);
		switch(ch)
		{
		case 1:
			pthread_cond_signal(&cond1);
			usleep(100);
			break;
		case 2:
			pthread_cond_signal(&cond2);
			usleep(100);
			break;
		case 3:
			pthread_cond_signal(&cond3);
			usleep(100);
			break;
		case 4:
			pthread_cond_broadcast(&cond1);
			pthread_cond_broadcast(&cond2);
			pthread_cond_broadcast(&cond3);
			usleep(100);
			break;
		case 0:
			pthread_mutex_lock(&mutex);
			pthread_cond_broadcast(&cond1);
			pthread_cond_broadcast(&cond2);
			pthread_cond_broadcast(&cond3);
			pthread_cond_destroy(&cond1);
			pthread_cond_destroy(&cond2);
			pthread_cond_destroy(&cond3);
			pthread_mutex_unlock(&mutex);

			pthread_mutex_destroy(&mutex);
			exit(0);
		}
	}

	return 0;
}

作业:

1:两车过隧道

有一个隧道,长1000m,有一辆高铁,每秒100米,有一辆快车,每秒50m
    要求模拟这两列火车通过隧道的场景

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

#define TUNNEL_LENGTH 1000  // 隧道长度

typedef struct {
    const char *name;  // 火车名称
    int speed;         // 火车速度(米/秒)
    int position;      // 火车当前位置
} Train;

void *simulate_train(void *arg) {
    Train *train = (Train *)arg;

    while (train->position < TUNNEL_LENGTH) {
        train->position += train->speed;  // 火车前进
        if (train->position > TUNNEL_LENGTH)
            train->position = TUNNEL_LENGTH;  // 防止超出隧道长度

        printf("%s 在 %dm\n", train->name, train->position);
        sleep(1);  // 模拟每秒前进
    }

    printf("%s has exited the tunnel.\n", train->name);
    return NULL;
}

int main() {
    pthread_t high_speed_thread, fast_train_thread;

    // 初始化两列火车
    Train high_speed_train = {"High-Speed Train", 100, 0};
    Train fast_train = {"Fast Train", 50, 0};

    // 创建线程模拟火车运行
    pthread_create(&high_speed_thread, NULL, simulate_train, &high_speed_train);
    pthread_create(&fast_train_thread, NULL, simulate_train, &fast_train);

    // 等待两列火车完成运行
    pthread_join(high_speed_thread, NULL);
    pthread_join(fast_train_thread, NULL);

    printf("Both trains have exited the tunnel.\n");
    return 0;
}

2:三车过隧道

有一个隧道,长1000m,有一辆高铁,每秒100米,有一辆快车,每秒50m,有一辆慢车每秒25m要求模拟这两列火车通过隧道的场景,但是要求高铁最先过隧道,快车其次,慢车最后

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

#define TUNNEL_LENGTH 1000  // 隧道长度

typedef struct {
    const char *name;  // 火车名称
    int speed;         // 火车速度(米/秒)
    int position;      // 火车当前位置
    int order;         // 火车顺序(1:高铁,2:快车,3:慢车)
} Train;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int current_order = 1; // 当前允许进入隧道的火车顺序

void *simulate_train(void *arg) {
    Train *train = (Train *)arg;

    // 等待轮到当前火车进入隧道
    pthread_mutex_lock(&mutex);
    while (train->order != current_order) {
        pthread_cond_wait(&cond, &mutex);
    }
    pthread_mutex_unlock(&mutex);

    // 模拟火车通过隧道
    while (train->position < TUNNEL_LENGTH) {
        train->position += train->speed;  // 火车前进
        if (train->position > TUNNEL_LENGTH) {
            train->position = TUNNEL_LENGTH;  // 防止超出隧道长度
        }

        printf("%s 在 %dm\n", train->name, train->position);
        sleep(1);  // 模拟每秒前进
    }

    printf("%s 已出隧道.\n", train->name);

    // 通知下一辆火车
    pthread_mutex_lock(&mutex);
    current_order++;
    pthread_cond_broadcast(&cond);
    pthread_mutex_unlock(&mutex);

    return NULL;
}

int main() {
    pthread_t threads[3];

    // 初始化三列火车
    Train high_speed_train = {"High-Speed Train", 100, 0, 1};
    Train fast_train = {"Fast Train", 50, 0, 2};
    Train slow_train = {"Slow Train", 25, 0, 3};

    // 创建线程模拟火车运行
    pthread_create(&threads[0], NULL, simulate_train, &high_speed_train);
    pthread_create(&threads[1], NULL, simulate_train, &fast_train);
    pthread_create(&threads[2], NULL, simulate_train, &slow_train);

    // 等待所有线程完成
    for (int i = 0; i < 3; i++) {
        pthread_join(threads[i], NULL);
    }


    return 0;
}


    
3:苹果生产

使用条件变量实现一个生产者消费者模型(pv)模型
    生产者线程:每秒生成2个苹果
    消费者线程:没3秒消费 5~9个苹果
    要求消费者在消费之前一定要有足够的苹果给消费  

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

int apple_count = 0;       // 当前苹果数量
int max_apples;            // 仓库最大容量
int producer_speed;        // 生产者生产速度(每秒生成的苹果数)
int consumer_speed;        // 消费者消费速度(每次消费间隔秒数)
pthread_mutex_t mutex;     // 互斥锁
pthread_cond_t cond;       // 条件变量

// 生产者线程函数
void *producer(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex);

        // 生产苹果
        if (apple_count + producer_speed <= max_apples) {
            apple_count += producer_speed;
            printf("生产了 %d 苹果. 总数: %d\n", producer_speed, apple_count);
        } else {
            printf("库存已满...\n");
        }

        // 唤醒消费者
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);

        sleep(1); // 模拟每秒生产
    }
    return NULL;
}

// 消费者线程函数
void *consumer(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex);

        int consumption = rand() % 5 + 5; // 随机消费 5~9 个苹果
        while (apple_count < consumption) {
            printf("消费者想要 %d 苹果, 但仅剩 %d 可用.\n", consumption, apple_count);
            pthread_cond_wait(&cond, &mutex); // 等待生产者生产
        }

        // 消费苹果
        apple_count -= consumption;
        printf("买了 %d 苹果. 剩余: %d\n", consumption, apple_count);

        pthread_mutex_unlock(&mutex);

        sleep(consumer_speed); // 模拟消费间隔
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    // 动态初始化参数
    printf("输入最大存储量: ");
    scanf("%d", &max_apples);
    printf("输入速度 (苹果/秒): ");
    scanf("%d", &producer_speed);
    printf("输入使用者间隔 (seconds): ");
    scanf("%d", &consumer_speed);

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    // 创建生产者和消费者线程
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    // 等待线程完成(实际上是死循环,不会退出)
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

标签:int,内附,互斥,死锁,train,cond,pthread,mutex,include
From: https://blog.csdn.net/weixin_56261190/article/details/145145562

相关文章

  • 【mysql数据库链接死锁怎么解决】
    mysql数据库链接死锁怎么解决在MySQL数据库中,死锁是指两个或多个事务相互等待对方释放资源,从而导致这些事务都无法继续执行的情况。解决MySQL中的死锁问题通常包括预防和处理两方面:预防死锁优化事务:尽量保持事务简短,减少持有锁的时间。避免长时间运行的事务。访问资源的......
  • 【操作系统---Linux】Linux编程中最常用的控制线程的函数(内附手绘底层逻辑图 通俗易懂
    绪论​每日激励:“不设限和自我肯定的心态:Icandoallthings。—StephenCurry”绪论​:本章是继承上一章线程基础,本章将结合代码和逻辑图的方式带你去认识和了解控制线程中常用的函数这些函数对后面的开发以及对线程底层的了解都非常的重要,后续将继续更新Linux线......
  • C++中线程同步与互斥的4种方式介绍、对比、场景举例
    在C++中,当两个或更多的线程需要访问共享数据时,就会出现线程安全问题。这是因为,如果没有适当的同步机制,一个线程可能在另一个线程还没有完成对数据的修改就开始访问数据,这将导致数据的不一致性和程序的不可预测性。为了解决这个问题,C++提供了多种线程同步和互斥的机制。1.......
  • 超级实用!优化Fiddler抓包默认生成的html报告(内附源码)
    Fiddler实现把抓包结果导出到html报告中之前写了一篇文章关于如何把Fiddler抓包的结果保存到html报告中,具体可以参考文章一文带大家了解如何在Fiddler中生成html测试报告_fiddler导出测试报告-CSDN博客报告整体效果如下所示很显然这个报告的易读性是较差的,最近我把这个......
  • c语言——【linux】多线程编程 (内附练习及代码)
    1:开启一个线程主线程中:使用标准IO,向一个文件中写入任意数据分支线程:使用标准IO,读取该文件中的数据#include<stdio.h>#include<string.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<p......
  • MySQL 死锁
    死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。1数据库层面解决死锁的两种方式1、解决死锁的问题最简单的方式是不要有等待,将任何的等待都转化为回滚,并且事务重新开始。 这种没有死锁问题的产生。在线上环境中,可能导致并发性能的下降,甚......
  • Google AI 智能体白皮书,超详细解读(内附下载)
    2AGI.NET|探索AI无限潜力,2AGI为您带来最前沿资讯。扫码加入2AGI技术社区!本文深入探讨了生成式AI智能体的核心组件、工作原理、关键技术及其广泛应用。文章从智能体的定义出发,详细介绍了其模型、工具和编排层的协同作用,以及认知架构的运作机制。同时,文章还讨论了如......
  • 【操作系统】课程 4调度与死锁 同步测练 章节测验
    4.1知识点导图处理机调度与死锁相关内容的文字整理:基本准则资源利用率:使系统中的处理机和其他所有资源都尽可能地保持忙碌状态。系统吞吐量:单位时间内系统所完成的作业数。公平性:使各进程都获得合理的CPU时间,而不会发生进程饥饿现象。响应时间:要尽可能短。周转时间:周转时......
  • 第25章 汇编语言--- 信号量与互斥锁
    信号量(Semaphore)和互斥锁(Mutex,全称MutualExclusionObject)是两种用于管理对共享资源的访问的同步机制。它们在多线程或多进程编程中非常重要,可以确保同一时间只有一个线程或进程能够访问特定的资源,从而避免了竞争条件(RaceCondition)。下面我将详细叙述这两种机制,并给出简单......
  • 详解SonarQube Web API的使用方法以及典型应用场景(内附python代码)
    SonarQubeWebAPISonarQube的WebAPI是一组HTTPRESTAPI,允许开发人员与SonarQube服务器进行交互。这些API涵盖了SonarQube的各个方面,包括项目管理、问题管理、质量规则和指标等。我们可以在SonarQube的帮助菜单中查看相关使用信息,如下图所示:典型应用场景SonarQubeAPI可......