【线程】线程同步

news2025/7/29 6:31:13

目录

一、信号量

1.函数

2.使用

二、读写锁

1.函数

2.使用

三、互斥锁

1.函数

2.使用

四、条件变量

1.函数

2.使用

前言

线程同步的实现方法:信号量、互斥锁、条件变量、读写锁。

下面就对着四种方法进行展开描述


一、信号量

与进程间通信的信号量类似,参考链接:【Linux】进程间通信——信号量

1.函数

头文件

#include<semaphore.h>

 sem_init()

int sem_init(sem_t *sem,int pshared,unsigned int value);
  • 作用:初始化由sem指向的信号量对象
  • 参数:pshared控制信号量类型,如果为0表示这个信号量是当前进程的局部信号量,否则,这个信号量就可以在多个进程之间共享。
  • 返回值:成功返回0

sem_post()

int sem_post(sem_t *sem);
  • 作用:原子操作的方式给信号量的值+1。所谓原子操作是指,如果两个线程企图同时给一个信号量加1,他们之间不会互相干扰,而不像如果两个程序同时对同一个文件进行读取、增加、写入操作时可能会引起冲突。信号量的值总是被正确地加2,因为有两个线程企图改变它。  
  • 参数:指针作为参数,指向对象是由sem_init调用的初始化信号量。
  • 返回值:成功返回0

sem_wait()

int sem_wait(sem_t *sem);
  • 作用:原子操作的方式给信号量的值-1。但他会等待直到信号量有个非零值才会开始减法操作。因此,如果对值为2的信号量调用sem_wait,线程将继续执行,但信号量的值会减到1.如果对值为0的信号量进行操作,sem_wait函数就会进行等待,直到有其他线程增加了该信号量的值使其不再是0为止。如果两个线程同时在sem_wait调用上等待同一个信号量变为非0值,那么该信号量被第三个线程增加1时,只有其中一个等待线程将开始对信号量-1,然后继续执行,另一个线程还将继续等待。
  • 参数:指针作为参数,指向对象是由sem_init调用的初始化信号量
  • 返回值:成功返回0

sem_destroy()

int sem_destroy(sem_t *sem);
  •  作用:用完信号量之后对它进行清理。
  • 指针作为参数,指向对象是由sem_init调用的初始化信号量,并清理该信号量拥有的所有资源。
  • 返回值:如果企图清理的信号量正在被一些线程等待,就会收到一个错误。成功返回0。

2.使用

用代码实现打印机对ABC顺序打印的操作,如下图所示,使用信号量完成代码:

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

sem_t sema;
sem_t semb;
sem_t semc;

void* funa(void* arg)
{
	for(int i=0;i<5;i++)
	{
		sem_wait(&sema);//ps1
		printf("A");
		fflush(stdout);
		sem_post(&semb);//vs2
	}
}
void* funb(void* arg)
{
	for(int i=0;i<5;i++)
	{
		sem_wait(&semb);
		printf("B");
		fflush(stdout);
		sem_post(&semc);
	}
}
void* func(void* arg)
{
	for(int i=0;i<5;i++)
	{
		sem_wait(&semc);
		printf("C");
		fflush(stdout);
		sem_post(&sema);
	}
}

int main()
{
	//初始化信号量
	sem_init(&sema,0,1);
	sem_init(&semb,0,0);
	sem_init(&semc,0,0);
	
	//创建线程
	pthread_t id1,id2,id3;
	pthread_create(&id1,NULL,funa,NULL);	
	pthread_create(&id2,NULL,funb,NULL);
	pthread_create(&id3,NULL,func,NULL);

	pthread_join(id1,NULL);
	pthread_join(id2,NULL);
	pthread_join(id3,NULL);

	sem_destroy(&sema);
	sem_destroy(&semb);
	sem_destroy(&semc);
	exit(0);
}

二、读写锁

由上面的程序可知,信号量的使用是A在读取时,阻止B,C的读取,这样拉低程序性能,读取的时候其实可以三个一起读,而在写操作的时候不能读,因此引入读写锁进行优化

1.函数

pthread_rwlock_init();//初始化

pthread_rwlock_destroy();//销毁

pthread_rwlock_rdlock();//读

pthread_rwlock_wrloc();//读写

pthread_rwlock_unlock();//解锁

要求:

  • 在写时,不能读
  • 在读时,不能读
  • 读的话都可以读

2.使用

代码:
读锁阻止写锁

写锁阻止读锁和写锁

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

pthread_rwlock_t lock;

void* fun1(void* arg)
{
	for(int i=0;i<5;i++)
	{
		pthread_rwlock_rdlock(&lock);
		printf("fun1 read start----\n");
		sleep(1);
		printf("fun1 over\n");
		pthread_rwlock_unlock(&lock);
		sleep(1);
	}
}
void* fun2(void* arg)
{
	for(int i=0;i<5;i++)
	{
		pthread_rwlock_rdlock(&lock);
		printf("fun2 read start----\n");
		sleep(2);
		printf("fun2 over\n");
		pthread_rwlock_unlock(&lock);
	}
}
void* fun3(void* arg)//写
{
	for(int i=0;i<5;i++)
	{
		pthread_rwlock_wrlock(&lock);
		printf("fun3 read start----\n");
		sleep(3);
		printf("fun3 over\n");
		pthread_rwlock_unlock(&lock);
	}
}
int main()
{
	pthread_rwlock_init(&lock,NULL);
	pthread_t id1,id2,id3;
	pthread_create(&id1,NULL,fun1,NULL);	
	pthread_create(&id2,NULL,fun2,NULL);
	pthread_create(&id3,NULL,fun3,NULL);
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);
	pthread_join(id3,NULL);
	pthread_rwlock_destroy(&lock);
	exit(0);
}

三、互斥锁

用的时候加锁,如果已经被别的进程加锁,那么该进程就只能被阻塞着。

互斥锁可以理解为信号量的子集

1.函数

头文件

#include<pthread.h>

返回值:成功返回0,失败返回错误码 

参数:与信号量类似,都是一个提前声明过的对象指针。对互斥锁来说,这个对象的类型为pthread_mutex_t。

pthread_mutex_init——初始化

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);

用于设置互斥量的属性,控制互斥量的行为

pthread_mutex_lock——加锁

int pthread_mutex_lock(pthread_mutex_t *mutex);

pthread_mutex_unlock——解锁

int pthread_mutex_unlock(pthread_mutex_t *mutex);

pthread_mutex_destroy——销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

2.使用

用代码实现互斥锁的使用,在打印机内A使用前加锁,使用后解锁,B在A使用的时候阻塞,如下图·所示:

代码:

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

pthread_mutex_t mutex;

void* fun1(void* arg)
{
	for(int i=0;i<5;i++)
	{
		pthread_mutex_lock(&mutex);
		printf("A");
		fflush(stdout);
		int n=rand()%3;
		sleep(n);
		printf("A");
		fflush(stdout);
		pthread_mutex_unlock(&mutex);
		n=rand()%3;
		sleep(n);
	}
}

void* fun2(void* arg)
{
	for(int i=0;i<5;i++)
	{
		pthread_mutex_lock(&mutex);
		printf("B");
		fflush(stdout);
		int n=rand()%3;
		sleep(n);
		printf("B");
		fflush(stdout);
		pthread_mutex_unlock(&mutex);
		n=rand()%3;
		sleep(n);
	}
}

int main()
{
	pthread_mutex_init(&mutex,NULL);//初始化
	pthread_t id1,id2;
 	pthread_create(&id1,NULL,fun1,NULL);
 	pthread_create(&id2,NULL,fun2,NULL);
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);	
	pthread_mutex_destroy(&mutex);//销毁
	exit(0);
}

运行结果:

四、条件变量

条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待 这个共享数据的线程。

唤醒时,如果线程不在等待队列中的(条件变量为空),此时唤醒就无意义

1.函数

头文件

#include<pthread.h>
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr); 
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_signal(pthread_cond_t *cond); //唤醒单个线程 
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒所有等待的线程
int pthread_cond_destroy(pthread_cond_t *cond);

2.使用

下面代码举例说明funa使用时加锁,用完解锁。funb使用时加锁,用完解锁。键盘输入数据唤醒funa,funb其中一个进行读取。如果键盘输入为end,funa,funb同时被唤醒,退出进程。

加锁的目的防止被他人唤醒被他人加入等待队列

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

pthread_mutex_t mutex;
pthread_cond_t cond;

void* funa(void* arg)
{
	char*s=(char*)arg;//退化为指向数组首地址指针
	while(1)//先阻塞,直到被唤醒
	{
		pthread_mutex_lock(&mutex);//加锁 用互斥锁保护
		pthread_cond_wait(&cond,&mutex);//加锁,解锁
		pthread_mutex_unlock(&mutex);//解锁

		if(strncmp(s,"end",3)==0)
		{
			break;
		}
		printf("funa: %s",s);
	}
}
void* funb(void* arg)
{
        char*s=(char*)arg;//退化为指向数组首地址指针
        while(1)//先阻塞,直到被唤醒
        {
                pthread_mutex_lock(&mutex);//加锁 用互斥锁保护
                pthread_cond_wait(&cond,&mutex);//加锁,解锁
                pthread_mutex_unlock(&mutex);//解锁

                if(strncmp(s,"end",3)==0)
                {
                        break;
                }
                printf("funb: %s",s);
        }
}

int main()
{
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);
	char buff[128]={0};
	pthread_t id1,id2;
	pthread_create(&id1,NULL,funa,(void*)buff);
	pthread_create(&id2,NULL,funb,(void*)buff);
	while(1)
	{
		fgets(buff,128,stdin);
		if(strncmp(buff,"end",3)==0)
		{
			//唤醒所有线程
			pthread_cond_broadcast(&cond);
			break;
		}
		else
		{
			//唤醒一个线程
			pthread_cond_signal(&cond);
		}

	}
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/16322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JNPF3.4.5大版本正式上线啦!

千呼万唤始出来&#xff0c;时隔近四个月&#xff0c;引迈信息终于再度推出新版本与大家见面了&#xff0c;此次推出的3.4.5大版本&#xff0c;可谓是吊足了大家的胃口。 本次大更新为用户带来的是高效率、高可用性、低成本、快速部署、易于扩展的快速开发平台的使用体验。本次…

【ASM】字节码操作 工具类与常用类 TraceClassVisitor 介绍

文章目录 1.概述2. TraceClassVisitor2.1 class info2.2 字段信息2.3 constructors2.4 methods3.如何使用TraceClassVisitor3.1 生成新的类3.2 修改已有的类3.3 打印ASM信息4.总结感谢第一个订阅字节码的人,感谢老铁支持 adminhjy 1.概述 在上一篇文章:

拓展卡尔曼滤波(Kalman)附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

分享5个良心好用的PC软件,免费无广告

今天再次推荐5个良心好用的Windows神级软件&#xff0c;每一个都是完全免费&#xff0c;堪称神器&#xff0c;让你打开新世界的大门。 1.数据恢复工具——EaseUS Data EaseUS Data Recovery Wizard是一款简单易用的数据恢复工具&#xff0c;给用户提供了三种恢复模式&#xf…

docker部署ES及kibana整个流程

对于ES小白&#xff0c;第一次安装ES走了很多弯路&#xff0c;下面记录自己本地安装elasticsearch的整个过程&#xff0c;我觉得小白如果按照我的流程走&#xff0c;大部分应该可以安装并运行成功。下面是整个步骤&#xff1a; 一、部署ES 拉取镜像 docker pull docker.elast…

胞外囊泡代谢组学—前列腺癌代谢变化研究的新策略

小小身体蕴含大大能量&#xff01;前列腺癌非侵入性的诊断和监测的新方式——胞外囊泡&#xff08;外泌体&#xff09;代谢组学&#xff01;目前&#xff0c;胞外囊泡/外泌体作为非侵入性的癌生物标志物已成为新的研究热点。百趣代谢组学文献分享&#xff0c;芬兰赫尔辛基大学学…

避免项目资源管理陷阱,8Manage帮你支招!

项目资源管理主要是对项目所需的人力、材料、机械、技术、资源等资源进行计划、组织、指挥、协调和控制。众所周知&#xff0c;项目推进需要资源支撑&#xff0c;一旦资源不足&#xff0c;项目的进度和质量都会受到影响。而在项目管理活动中&#xff0c;做好资源管理并不容易&a…

SORT追踪

卡尔曼滤波 卡尔曼滤波用当前时刻运动变量去预测下一时刻的运动变量&#xff0c;检测器第一次的检测结果用来初始化卡尔曼滤波的运动变量&#xff0c;后续的结果作为更新。在信号处理中卡尔曼滤波是去除噪声的一个算法&#xff0c;作用是使用信号更加的准确。在SORT中的&#x…

MySQL常用语句汇总

一、背景 日常测试开发工作中会用到各类SQL语句&#xff0c;很多时候都是想用的时候才发现语句细节记不清楚了&#xff0c;临时网上搜索SQL语法&#xff0c;挺费时费力的&#xff0c;语法还不一定是对的。因此汇总整理了一下MySQL最常用的各类语句&#xff0c;以后就不用再到处…

Linux环境安装

学习Linux首先要准备一个Linux环境。环境的安装有两种途径&#xff1a;买一个云服务器&#xff0c;安装虚拟机。 推荐使用云服务器&#xff0c;较虚拟机方便很多。 云服务器具体来说是Centos 7.6 64位——我也不知道为啥用这个 步骤一&#xff1a; 购买云服务器的主要方式…

“向美好女人致敬”粉红丝带主题活动,谈水果养生之道

传递粉红正能量&#xff0c;践行粉红关爱&#xff0c;“向美好女人致敬”粉红丝带关爱月公益线下活动于11月13日顺利收尾&#xff0c;帮助广大女性更加深入地了解、认识乳腺癌预防和康复治疗&#xff0c;推进乳腺癌防治意识。此次活动邀请到了云南省肿瘤医院乳腺三科科主任、副…

Android通过jni调用本地c/c++接口方法总结

网上有网友问android的原生应用,上层java代码如何通过jni调用本地的c/c++接口或第三方动态库 ?之前搞过android应用开发和底层c/c++接口开发都是一个人搞定,觉得还是蛮简单的。其实没啥难度,如果觉得难只是因为你没有经历过,只要搞过一遍基本就记住了。这里总结下方法留作…

李嘉诚人生最大的错误,并非错过阿里华为,而是套现中国投资欧洲

李嘉诚是很多人心中的生意之神&#xff0c;很多人认为李嘉诚一生从来都没有失败过&#xff0c;他是生意场的常胜将军。可是事实上真的是如此么&#xff1f; 很多人可能不知道&#xff0c;李嘉诚其实也曾经犯下了很大的错误&#xff0c;比如说2003年前后&#xff0c;李嘉诚先后错…

【设计模式】2.工厂模式

文章目录1. 工厂模式概述2. 简单工厂模式3. 工厂方法模式4. 抽象工厂模式1. 工厂模式概述 工厂模式属于创建型模式的一种。 在java中&#xff0c;万物皆对象&#xff0c;这些对象都需要创建&#xff0c;如果创建的时候直接new该对象&#xff0c;就会对该对象耦合严重&#xf…

图神经网络学习笔记

1 图神经网络应用 芯片设计、场景分析问题推理、推荐系统、欺诈检测风控相关、道路交通动态流量预测、自动驾驶、无人机等、化学医疗等场景 2 图神经网络基本组成 点(vertex)、边(edge)、全局图(global)&#xff0c;图神经网络&#xff08;GNN&#xff0c;Graph Neural Netw…

Java 集合知识点总结

Java 集合知识点总结总览Collection 接口ListArrayList源码&扩容机制SetQueueMap接口HashMapHashMap源码&底层数据结构HashMap 的遍历LinkedHashMapTreeMapHashtableConcurrentHashMap 源码&底层数据结构本文是个人阅读学习JavaGuide的集合知识的总结笔记。总览 C…

【计算机毕业设计】个人交友网站源码

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘 要 本论文主要论述了如何使用JAVA语言开发一个个人交友网站&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0…

【最强最全车牌识别算法】支持13种中文车牌识别的云端API部署(可直接获取源码使用)

项目简介 在城市交通管理、视频监控、车辆识别和停车场管理中车辆检测与车牌识别是一项富有挑战而重要的任务。利用深度学习识别不同条件下的车辆及其车牌信息。更具体地说&#xff0c;实时目标检测网络&#xff08;Yolov5&#xff09;用于从车辆图像中提取特征并且通过训练对…

[附源码]java毕业设计面试刷题系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于分组码的消息验证码的程序实现

目 录 摘 要 I 1 绪论 1 1.1 课题研究背景和意义 1 1.2 课题研究现状 1 2 CBC MAC的特征和基本工作原理 2 2.1 CBC MAC的特征和码集选择的原则 2 2.2 CBC MAC生成原理 2 2.3 CBC MAC模块结构图 3 3 FPGA和VHDL语言 4 3.1 概述 4 3.2 VHDL语言特点 5 3.2.1 常用硬件描述语言简介…