线程同步的实现

news2024/11/29 2:38:38

线程同步

同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。

"同"字从字面上容易理解为一起动作

其实不是,"同"字应是指协同、协助、互相配合。

如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。

所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。例如Window API函数SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的LRESULT值返回给调用者。

在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。

线程同步的方式和机制

临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)的区别

1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。

2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享

3、信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目

4、事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作

 线程的实现一般包括用户级和内核级以及组合的情况 

但是组合的情况是比较理想化的

因此我们一般都是根据场景选择用户级线程创建或者内核级线程创建

他们的优缺点分别是

 

信号量处理线程同步

我们可以用信号量模拟一个线程的同步

例如我们要顺序输出五个abc

那么我们模拟这个线程是一个试衣间 0表示不能使用 1表示可以使用

然后 p v 操作时是对应的获取资源操作和释放资源操作

然后我们可以对abc三个试衣间进行循环的p v操作

这样就可以实现abc的顺序输出

那么三个试衣间 在这个场景下 我们就需要使用三个信号量来配合操作

 对于信号量方法不熟悉的可以浏览我的往期博文

 

 

这是线程内部函数 我们可以看到在循环中对信号量进行p v 操作即可实现有序化控制

 

 

我们可以看到对信号量初始化的时候我们对a的初始化为1

因我我们的要求是顺序输出ABC

那么就要从a开始 我们就将a的初始化赋值为1

然后在循环中我们不停的进行p v操作

这样就可以实现一个有序化输出

 互斥锁处理进程同步

在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

那么为什么要使用互斥锁这个概念呢

我们就要再次提到我们上一节中提到的

如果有五个线程 每一个线程都对一个val进行加加操作

那么我们预计输出的结果是五千

但是其实是有几率出现不是五千的情况 这是因为我们又可能会出现两个线程对一个val同时加加

那么就会出现少加的情况 这样得到的数就会小于5000

这个和处理器的数量有关系

如果是单核处理器 那么同一时间就只允许一个线程访问

那么就会按照规则完成5000次加加  最后得到的数据一定是5000

但是如果处理器数大于1 就有几率出现两个线程抢占 然后导致最后得到的结果不是5000

可以看到我使用的本地unbantu的配置的处理器数量为四

那么我们出现小于5000的概率就会大大提升

 

 

可以看到仅一次运行就出现我们描述的问题

那么我们 可以通过互斥锁来解决

锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

int pthread_mutex_lock(pthread_mutex_t *mutex)

int pthread_mutex_unlock(pthread_mutex_t *mutex)

int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

我们可以使用这个锁对关键语句进行操作

那么这段代码在运行时就是受到保护 便不会出现两个线程争抢的问题

 

 

我们可以看到运行多次都会是5000

这就是互斥锁的作用

那么关于互斥锁还有重要的一点是 互斥锁要加到最需要保护的代码段处

 

因为锁也会消耗时间 如果每次都在经历多段代码

那么代码运行成功的时间就会增加

 读写锁处理线程同步

ReadWriteLock同Lock一样也是一个接口,提供了readLock和writeLock两种锁的操作机制,一个是只读的锁,一个是写锁。ReentranReadWriteLock是其实现类

读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的(排他的)。 每次只能有一个写线程,但是可以有多个线程并发地读数据。

所有读写锁的实现必须确保写操作对读操作的内存影响。换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容。

理论上,读写锁比互斥锁允许对于共享数据更大程度的并发。与互斥锁相比,读写锁是否能够提高性能取决于读写数据的频率、读取和写入操作的持续时间、以及读线程和写线程之间的竞争。
 

读写锁和互斥锁的区别

1)读写锁区分读者和写者,而互斥锁不区分

2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。

读写锁是在读端或者写端的时候进行锁

那么我们读的时候读都是被允许的 写不被允许

相反 写的时候写都是允许的 而读不被允许

例如读写操作比较多的时候我们就可以使用读写锁来实现线程同步

读写锁的实现就是表现形式和互斥锁不同

其它的操作均可以模仿互斥锁

唯一需要注意的是就是要把读写锁要对应

在读端要使用读锁

写端使用使用写锁

在多线程的实现中 线程步是十分重要的 它关乎线程能否稳定安全的实现既定的功能

 

 

 

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

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

相关文章

USB子系统简述

引子:关于 lsusb 命令 lsusb 列出系统中所有的USB设备: Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hubBus 004 :表示第四个 usb 主控制器(机器上总共有四个 usb 主控制器,可以通过命令 lspci | g…

看完这篇文章终于弄明白了什么是 RocketMQ 的存储模型

RocketMQ 优异的性能表现,必然绕不开其优秀的存储模型 。这篇文章,笔者按照自己的理解 , 尝试分析 RocketMQ 的存储模型,希望对大家有所启发。1 整体概览首先温习下 RocketMQ 架构。整体架构中包含四种角色 :Producer :消息发布的…

基于Python深度学习的垃圾分类代码,用深度残差网络构建

垃圾分类 完整代码下载地址:基于Python深度学习的垃圾分类代码 介绍 这是一个基于深度学习的垃圾分类小工程,用深度残差网络构建 软件架构 使用深度残差网络resnet50作为基石,在后续添加需要的层以适应不同的分类任务模型的训练需要用生…

Qt扫盲-QSerialPort理论总结

QSerialPort理论总结一、概述二、使用流程1. 错误处理2. 阻塞串行端口编程3. 非阻塞串行端口编程三、信号四、注意事项一、概述 QSerialPort 类其实就是一个打开串口,进行串口通信传输数据的功能类。我们可以使用QSerialPortInfo帮助类获取有关可用串行端口的信息&…

JavaEE高阶---Spring AOP

一:什么是Spring AOP? 首先,AOP是一种思想,它是对某一类事情的集中处理。 如用户登录权限的效验,没学 AOP 之前,我们所有需要判断用户登录的页面,都要各自实现或调用验证的方法。然后有了 AOP …

【Linux进程间通信】

Linux进程间通信进程间通信介绍进程间通信的概念进程间通信的目的进程间通信的本质进程间通信的分类管道什么是管道匿名管道匿名管道的原理pipe函数匿名管道使用步骤匿名管道读写规则匿名管道的特点匿名管道的四种特殊情况匿名管道的大小命名管道命名管道的原理使用命令创建命名…

【浮点数在内存中的存储规则】

我们知道,整型在内存中的存储比较简单,在内存中都是以二进制来存储的。然而,浮点型在内存中的存储较为复杂。下面来详细探讨: 直接举一个例子: int main() { int n 9; float *pFloat (float *)&n; printf("…

工业树莓派解决传统数据设备数据上云问题

一、前言 工业4.0的浪潮下,许多中小型制造业企业渴望通过数字化转型谋求新的发展动力,然而,在转型之路上常常会面临一个问题:传统数据采集设备数量多、种类杂,不支持比较新颖的现场总线协议或者通信技术,最…

java 微服务框架介绍 SpringCloud Eureka注册中心 Nacos注册中心

为什么要学习微服务框架 认识微服务 服务架构演变 单体架构 分布式架构 微服务结构 SrpingCloud SpringCloud是目前国内使用最广泛的微服务框架。官网地址:https://spring.io/projects/spring-cloud。 服务拆分及远程调用 服务拆分注意事项 我们查询的时候需要…

Java基础 —— 编程入门

一、比特(bit)和字节(byte)一个0或者一个1存储为一个比特(bit),是计算机中最小的存储单位。计算机中是最基本的存储单元是字节(byte)。每个字节由8个比特构成。计算机就是一系列的电路开关。每个开关存在两种状态:关(off)和开(on)。如果电路是开的,它的值…

Ubuntu物理真机提高访问速度

这里不适合小白用户,只是做出几点提醒。 iguge学术助手 纯Ubuntu真机,是没办法访问外部网络的,先用百度搜索iguge下载一个,安装在Firefox浏览器插件上(edge或者chrome也行)。 免费的不好用,建…

算法之初始动态规划

目录 前言: 初始动态规划 0-1背包问题 0-1背包问题升级版 问题:如何巧妙解决“双十一”购物是的凑单问题? 总结: 前言: 淘宝的“双十一”购物节有各种促销活动,比如“满 200 元减 50 元”。假设你女朋友…

SpringBoot使用 axis 实现webservice客户端(亲测可行)

目录一、webservice在线验证服务端接口地址二、使用 axis 实现webservice客户端代码示例2.1、服务端地址使用qq在线接口验证接口2.2、webservice客户端示例代码一、webservice在线验证服务端接口地址 qq 在线验证接口:http://www.webxml.com.cn/webservices/qqOnli…

[飞腾]Trace32使用概述

最近将多年来收集到的教学视频、国内外图书、源码等整理整合拿出来,涉及arm、Linux、python、信号完整性、FPFA、DSP、算法、stm32、单片机、制图、电子模块、kali、出版社图书等。资料目前约1.5TB。资料详情请参阅: 1.5TB电子工程师资料详细介绍https:/…

软件定义的存储时代即将结束

数据存储、安全性、保护和整体管理对于大多数组织的生存至关重要。 从软件定义的存储时代的结束到本地存储的回归,Nyriad的首席营收官概述了他对最新技术趋势的看法,并提供了他对2023年将会发生的预测。 从以CPU为中心的软件定义存储过渡到卸载辅助架构…

Java 开发环境配置 || Java 基础语法

Java 开发环境配置 在本章节中我们将为大家介绍如何搭建Java开发环境,以及不同系统下的环境变量怎么配置。 window系统安装java 下载JDK 首先我们需要下载java开发工具包JDK,下载地址:Java Downloads | Oracle 点击如下下载按钮&#xff…

阿里CCO:基于Hologres的亿级明细BI探索分析实践

作者:张乃刚(花名:隽驰),CCO数据开发 CCO是Chief Customer Officer的缩写,也是阿里巴巴集团客户体验事业部的简称。随着业务的多元化发展以及行业竞争的深入,用户体验问题越来越受到关注。CCO体验业务运营…

【前端】CSS进阶

四、选择器进阶 1.1后代选择器:空格 作用:根据HTML标签的嵌套关系,选择父元素后代中满足条件的元素 选择器语法:选择器1 选择器2{css} 结果: 在选择器1所找到标签的后代(儿子、孙子、重孙子…&#xf…

Zipkin基础知识及Linux下搭建服务端

Zipkin组成 Zipkin的基础架构,他由4个核心组件构成:分别是Collector、Storage、RESTful API、WebUI Collector:收集器组件,它主要用于处理从外部系统发送过来的跟踪信息,将这些信息转换为 Zipkin 内部处理的 Span 格式…

最快速的获取元素的方法?快到你想象不到~

1、首先我们先准备一个div标签&#xff0c;id定义为box<div id"box"></div>2、通常情况下我们会使用原生js获取&#xff0c;如下所示&#xff1a;let box document.getElementById("box"); // 或者 let box document.querySelector("#b…