线程同步的方法1——互斥锁、信号量

目录

一、引入

二、利用多线程同步解决线程并发

三、线程同步的概念

四、互斥锁

4.1互斥锁接口

4.2全局变量++正确性问题(引例)

4.3 互斥锁例2(共享资源(打印机)使用问题)

五、信号量

5.1 信号量接口

5.2 全局变量++正确性问题

5.3 信号量例2


一、引入

创建5个线程,修改和读取全局变量,代码如下:

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


int wg=0;
void* fun(void* arg)
{
	for(int i=0;i<1000;i++)
	{
		wg++;
		printf("wg=%d\n",wg);
	}
}

int main()
{
	pthread_t id[5];
	for(int i=0;i<5;i++)
	{
		pthread_create(&id[i],NULL,fun,NULL);
	}
	for(int i=0;i<5;i++)
	{
		pthread_join(id[i],NULL);
	}
	exit(0);
}

运行结果: 

正确结果应该是5000,程序运行结果错误。

原因:因为wg++不是一个原子操作;

原子操作的概念:一个或多个指令的序列,对外是不可分的;即没有其他进程可以看到其中间状态或者中断此操作;

而wg++不是一个原子操作;
这种情况也不是每一次都发生,也不是一定发生了多少次,这种情况就是最可怕的,给人的感觉是时对时错;

注意,只有一个处理器的时候这种情况出现的概率是非常小的,同一时刻只有一个线程在运行,不容易出现两个线程同时去获取i的值的情况,但是也会发生; 

二、利用多线程同步解决线程并发

多线程并发就有可能出现问题,比如两个线程都去在链表中插入,比如都在做尾插,都在找尾巴,那么多线程就会出现问题。

三、线程同步的概念


一个进程中的所有线程共享同一个地址空间和诸如打开的文件之类的其他资源.一个线程对资源的任何修改都会影响同一个进程中其他线程的环境,因此,需要同步各种线程的活动,以便它们互不干涉且不破坏数据结构.例如,如果两个线程都试图同时往一个双向链表中增加一个元素,则可能会丢失一个元素或者破坏链表结构.

同步就是让所有线程按照一定的规则执行,使得其正确性和效率都有迹可循,线程同步的手段就是对线程之间的穿插进行控制.

线程同步指的是当一个线程在对某个临界资源进行操作时,其他线程都不可以对这个资源进行操作,直到该线程完成操作,其他线程才能操作,也就是协同步调,让线程按预定的先后次序进行运行。 

线程同步的方法:

  • 互斥锁
  • 信号量
  • 条件变量、
  • 读写锁

四、互斥锁

4.1互斥锁接口

int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);//attr:锁的属性,不需要传空即可

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread mutex_t *mutex);

int pthread_mutex_destroy(pthread mutex_t *mutex);

注意,互斥锁mutex都需要传地址,因为要改变它;

4.2全局变量++正确性问题(引例)

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


int wg=0;

pthread_mutex_t mutex;
void* fun(void* arg)
{
	for(int i=0;i<1000;i++)
	{
		pthread_mutex_lock(&mutex);
		wg++;
		pthread_mutex_unlock(&mutex);
		printf("wg=%d\n",wg);
	}
}

int main()
{
	pthread_t id[5];
	pthread_mutex_init(&mutex,NULL);
	for(int i=0;i<5;i++)
	{
		pthread_create(&id[i],NULL,fun,NULL);
	}
	for(int i=0;i<5;i++)
	{
		pthread_join(id[i],NULL);
	}
	pthread_mutex_destroy(&mutex);
	exit(0);
}

4.3 互斥锁例2(共享资源(打印机)使用问题)

主线程和函数线程模拟访问打印机,主线程输出第一个字符'A'表示开始使用打印机,输出第二个字符'A'表示结束使用,函数线程操作与主线程相同。

(由于打印机同一时刻只能被一个线程使用,所以输出结果不应该出现ABAB交替出现)

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

pthread_mutex_t mutex;
void* thread_fun(void* arg)
{
	for(int i=0;i<5;i++)
	{
		pthread_mutex_lock(&mutex);
		write(1,"B",1);
		int n=rand()%3;	
		sleep(n);
		write(1,"B",1);
		pthread_mutex_unlock(&mutex);

		 n=rand()%3;
		sleep(n);
	}
	pthread_exit(NULL);
}

int main()
{
	pthread_t id;
	pthread_mutex_init(&mutex,NULL);
	pthread_create(&id,NULL,thread_fun,NULL);
	for(int i=0;i<5;i++)
	{
		pthread_mutex_lock(&mutex);
		write(1,"A",1);
		int n=rand()%3;
		sleep(n);
		write(1,"A",1);
		pthread_mutex_unlock(&mutex);

		n=rand()%3;
		sleep(n);
	}
	pthread_mutex_destroy(&mutex);
	pthread_join(id,NULL);
	exit(0);
}

五、信号量

5.1 信号量接口

sem_t 全局定义一个sem_t类型的信号量。

注意,必须要加头文件:#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);//信号量的初始化

作用:在sem指向的地址初始化未命名的信号量。

value参数指定信号量的初始值(第三个参数);

pshared:设置信号量是否在进程间共享,Linux不支持,一般给0;(非0为共享) 

int sem wait(sem t*sem)://P操作,wait表示等待,相当于是等待获取资源,那么就是P操作

int sem_post(sem_t *sem);//V操作

int sem_destroy(sem_t *sem);//销毁信号量

5.2 全局变量++正确性问题

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
int wg=0;
sem_t sem;
void* fun(void* arg)
{
	for(int i=0;i<1000;i++)
	{
		sem_wait(&sem);
		wg++;
		sem_post(&sem);
		printf("wg=%d\n",wg);
	}
}

int main()
{
	pthread_t id[5];
	sem_init(&sem,0,1);
	for(int i=0;i<5;i++)
	{
		pthread_create(&id[i],NULL,fun,NULL);
	}
	for(int i=0;i<5;i++)
	{
		pthread_join(id[i],NULL);
	}
	sem_destroy(&sem);
	exit(0);
}

5.3 信号量例2

主线程获取用户输入,函数线程将用户输入的数据存储到文件中。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <fcntl.h>
sem_t sem1;
sem_t sem2;
char buff[128]={0};

void* PthreadFun(void* arg)
{
	int fd=open("a.txt",O_RDWR|O_CREAT,0664);
	assert(fd!=-1);

	while(1)
	{
		sem_wait(&sem2);
		if(strncmp(buff,"end",3)==0)
		{
			break;
		}
		write(fd,buff,strlen(buff));
		memset(buff,0,128);
		sem_post(&sem1);
	}
	sem_destroy(&sem1);
	sem_destroy(&sem2);
}

int main()
{
	sem_init(&sem1,0,1);
	sem_init(&sem2,0,0);

	pthread_t id;
	int res=pthread_create(&id,NULL,PthreadFun,NULL);
	assert(res==0);

	while(1)
	{
		sem_wait(&sem1);
		printf("please input data:");
		fflush(stdout);

		fgets(buff,128,stdin);
		buff[strlen(buff)-1]=0;
		sem_post(&sem2);
		if(strncmp(buff,"end",3)==0)
		{
			break;
		}
	}
	pthread_exit(NULL);
}

运行结果: 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前方高能,又一波Smartbi签约喜报来袭

近期&#xff0c;交通银行、厦门国际银行、中原农业保险、江苏中天科技等多家知名企业签约Smartbi&#xff0c;携手Smartbi实现数据驱动业务新增长。 Smartbi数10年专注于商业智能BI与大数据分析软件与服务&#xff0c;为各行各业提供提供一站式商业智能平台&#xff08;PaaS&a…

阿里云老用户可以购买99元服务器,2核2G3M固定带宽,你说牛不牛?

2024阿里云服务器优惠活动政策整理&#xff0c;阿里云99计划ECS云服务器2核2G3M带宽99元一年、2核4G5M优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M服务器61元一年、2核4G4M带宽165元1年&#xff0c;云服务器4核16G10M带宽26元1个月、149元半年&#xff0c;云服务器8核…

域名 DNS 信息查询 API 数据接口

域名 DNS 信息查询 API 数据接口 网络工具&#xff0c;多种记录类型数据返回&#xff0c;丰富的信息结构&#xff0c;毫秒级响应。 1. 产品功能 提供域名 DNS 解析完整记录&#xff1b;丰富的解析记录类型&#xff0c;包括&#xff1a;A, AAAA, MX, TXT, NS, CNAME, SRV, PTR…

pgvector docker部署测试

docker pull pgvector/pgvector:pg16 运行 docker run --name pgvector --restartalways -e POSTGRES_USERpgvector -e POSTGRES_PASSWORDpgvector -v /srv/tlw/pgvectordata:/var/lib/postgresql/data -p 54333:5432 -d pgvector/pgvector:pg16 CREATE EXTENSION vector; --…

ORACLE 如何使用dblink实现跨库访问

dbLink是简称&#xff0c;全称是databaselink。database link是定义一个数据库到另一个数据库的路径的对象&#xff0c;database link允许你查询远程表及执行远程程序。在任何分布式环境里&#xff0c;database都是必要的。另外要注意的是database link是单向的连接。在创建dat…

重装显卡驱动记录

重装显卡驱动记录 任务记录现状描述执行情况 任务 晚上回来&#xff0c;开电脑&#xff0c;发现总是进不去系统&#xff08;这个情况我经常见&#xff09;&#xff0c;但偶尔进系统&#xff0c;识别不了我的外接屏&#xff08;这个第一次见&#xff09;。来来回回重启了1h多了…

QT安装教程,手把手教会QT安装

大家好&#xff1a;衷心希望各位点赞和评论&#xff01; 安装步骤 首先去官网下载QT&#xff0c;官网&#xff1a;https://download.qt.io/https://download.qt.io/ 点击 offical_releases &#xff08;官方发行版本&#xff09;&#xff0c;如下图所示&#xff1a; 进入下图所…

【C++】102.二叉树的层序遍历

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]]示例 2&#xff1…

反射面试题

反射的优点&#xff1a;提高Java程序的灵活性和扩展性&#xff0c;降低了耦合性&#xff0c;提高自适应能力。 允许创建和控制任意类对象&#xff0c;无需提前硬编码目标类 缺点&#xff1a; 反射的性能低 反射机制主要在对灵活性和扩展性要求很高的系统框架上。 放射会模糊内部…

【C++入门】引用

目录 6.引用 6.1引用概念 6.2引用的写法 6.3引用的特性 6.4常引用 6.5引用的使用场景 6.5.1引用做参数 6.5.2引用做返回值❗❗ &#x1f387;值做返回值 &#x1f387;引用做返回值 &#x1f387;引用在顺序表做返回值 6.5.3传值、传引用效率比较(参数&#xff0…

OSPF NSSA实验简述

OSPF NSSA实验简述 1、OSPF NSSA区域配置 为解决末端区域维护过大LSDB带来的问题&#xff0c;通过配置stub 区域或totally stub区域可以解决&#xff0c;但是他们都不能引入外部路由场景。 No so stuby area &#xff08;区域&#xff09;NSSA 可以引入外部路由&#xff0c;支持…

【Linux】ecs 挂载分区

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 详细步骤&#xff1a; 结语 我的其他博客 前言 在Linux系统中&#xff0c;挂载分区是连接额外存储空间到文件系统的重要步骤之一…

【计算机网络】IO多路转接之epoll

文章目录 一、epoll的相关系统调用二、epoll工作原理三、epoll的优点(和 select 的缺点对应)四、epoll工作方式五、epoll服务器1.Sock.hpp2.Log.hpp3.Err.hpp4.epollServer.hpp5.epollServer.cc 一、epoll的相关系统调用 按照man手册的说法: 是为处理大批量句柄而作了改进的po…

iOS小技能:苹果开发者申请材料

文章目录 引言I 个人账号申请资料II 公司账号申请所需资料III duns资料提交操作步骤IV 续费引言 https://developer.apple.com/cn/programs/enroll/ 申请过程只能使用同一台设备注册苹果开发者的Apple ID可以转让。注册苹果开发者的在验证身份证信息的时候,必须使用法定姓名拼…

信呼OA普通用户权限getshell方法

0x01 前言 信呼OA是一款开源的OA系统&#xff0c;面向社会免费提供学习研究使用&#xff0c;采用PHP语言编写&#xff0c;搭建简单方便&#xff0c;在中小企业中具有较大的客户使用量。从公开的资产治理平台中匹配到目前互联中有超过1W的客户使用案例。 信呼OA目前最新的版本是…

Docker_设置docker服务以及容器开机自启

本文目录 docker服务开机自启动查询docker服务开机自启动状态将docker服务设置为开机自启动取消docker服务开机自启动 容器开机自启动修改docker容器为自启动容器启动时设置自启动-docker版容器启动时设置自启动-docker-compose版 docker服务开机自启动 查询docker服务开机自启…

git 命令怎么回退到某个特定的 commit 并将其推送到远程仓库?

问题 不小心把提交的名称写错提交上远程仓库了&#xff0c;这里应该是 【029】的&#xff0c;这个时候我们想回到【028】这一个提交记录&#xff0c;然后再重新提交【029】到远程仓库&#xff0c;该怎么处理。 解决 1、首先我们找到【028】这条记录的提交 hash&#xff0c;右…

【web安全】实战 批量横扫springboot命令执行漏洞

天命&#xff1a;这次目标批量横扫&#xff0c;但是没完全成功&#xff0c;也没完全失败 步骤1&#xff1a;磨刀准备 这次先针对漏洞来寻找目标&#xff0c;所以寻找这种 springboot 的目标 利用CVE漏洞&#xff0c;进行命令执行攻击 先找靶场训练一波&#xff0c;叠加反弹sh…

2024年阿里云域名优惠口令更新,亲测有效口令大全

2024年阿里云域名优惠口令&#xff0c;com域名续费优惠口令“com批量注册更享优惠”&#xff0c;cn域名续费优惠口令“cn注册多个价格更优”&#xff0c;cn域名注册优惠口令“互联网上的中国标识”&#xff0c;阿里云优惠口令是域名专属的优惠码&#xff0c;可用于域名注册、续…

【教育部白名单赛事】C语言编程题解析--软件编程邀请赛(决赛)

文章目录 1、保留12位小数的浮点数2、气温统计3.大写字母的判断4、【递归】母鸡的故事5、小白免再排队 1、保留12位小数的浮点数 输入一个双精度浮点数&#xff0c;保留12位小数&#xff0c;输出这个浮点数。 时间限制&#xff1a;1000 内存限制&#xff1a;65536 【输入】 只…