线程池前置知识之线程同步

2024-06-14 23:12
文章标签 线程 知识 同步 前置

本文主要是介绍线程池前置知识之线程同步,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        在线程池中存在两个队列,一个任务队列用来缓存任务,一个线程队列用来缓存线程。在多线程环境下,需要考虑两个队列的线程同步问题。


线程互斥

        要判断一段代码是否能够被多线程执行,要看这段代码中是否存在竞态条件

        竞态条件(Race Condition)是指在并发环境中,当有多个线程或进程同时访问同一个临界资源时,由于多个线程的并发执行顺序的不确定,从而导致程序输出结果的不确定。这种情况发生在计算的正确性取决于多个线程的交替执行时序时。竞态条件可能导致程序运行顺序的改变,进而影响最终结果,产生超出预期的情况。 

        如果该代码片段存在竞态条件,那么则称这段代码区域为临界区,临界区是不能被并发访问且不可重入的,需要保证它的原子操作

        如果该代码片段不存在竞态条件,那么则称这段代码区域是可重入的。

        想要保证临界区的原子操作,可以使用互斥锁mutex或者原子类型atomic

        在进入临界区之前获取互斥锁、在离开临界区释放互斥锁,就能够保证在同一时间只有持有互斥锁的一个线程能够进入临界区执行代码而其他申请锁的线程只能挂起等待锁的释放,这样就能够保证临界区的原子操作。如果临界区不大,获取锁的速度非常快,也可以使用乐观锁来代替悲观锁,此时当持有锁的线程正在临界区执行代码,而其余线程在申请锁时就不会被挂起,而是一直申请锁,这样就不会让其余线程刚被阻塞挂起没多久就因锁被释放而唤醒。

        除了互斥锁,C++11还提供了CAS操作(无锁机制) atomic。无锁机制并不是不使用锁,而是使用一种基于活锁CAS(Compare And Swap)操作来实现。

        atomic可以使变量的++或--操作变成原子操作,那么如果临界区内只是对某个变量进行++或--操作,我们就可以将该变量变成atomic原子类型,这样就可以避免申请和释放锁的开销了。

#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
using namespace std;int main() {int i = 0;atomic<int> j = 0;vector<thread> threads;threads.emplace_back([&i](int n)->void {while (n--) { ++i; } }, 100000);threads.emplace_back([&i](int n)->void {while (n--) { ++i; } }, 100000);threads.emplace_back([&j](int n)->void {while (n--) { ++j; } }, 100000);threads.emplace_back([&j](int n)->void {while (n--) { ++j; } }, 100000);for (auto& thread : threads) {thread.join();}cout << "i = " << i << " " << "j = " << j << endl;return 0;
}


线程通信

        现有线程1、线程2两个执行流,线程1执行的任务1依赖于线程2执行的任务2得到的结果,虽然图上任务2的执行顺序在任务1之前,但是由于线程的调度完全是由系统内核的调度算法决定的,所以CPU先执行哪个任务是不确定的,为了保证任务2在任务1之前被执行,就需要线程之间进行通信,如果任务1先被调度执行就进入等待状态,等任务2执行完毕了再来继续执行。为了完成线程间通信,我们可以借助条件变量condition_variable信号量semaphore

        谈到线程间通信我们就需要想到多线程模型中的生产者消费者模型。 生产者消费者模型可以被称为“三二一原则”,

        三指的是三种关系

        ① 生产者与生产者之间的互斥关系;

        ② 生产者与消费者之间的互斥且同步关系;

        ③ 消费者与消费者之间的互斥关系。

        二指的是两种角色:生产者和消费者。

        一指的是一个交易场所:缓冲区。

        生产者消费者模型可以让生产者和消费者进行解耦,也支持并发和忙闲不均

        借助条件变量和互斥锁(条件变量必须要和互斥锁结合起来使用,这也使条件变量的控制精细程度比信号量要好),我们可以来实现生产者消费者模型:

        详细可以参考:

        用条件变量构建的阻塞队列来完成生产消费者模型

        信号量是一种用于控制对共享资源的访问的机制。它可以用来限制同时访问某个资源的线程数量。在上文中我们谈到一把互斥锁最多只能被一个线程所获取,那么我们就可以把互斥锁看作是一个资源计数只能是0或1的资源。那为什么现在要说这个呢?是因为我们可以把信号量看作一个资源计数没有限制的互斥锁,当信号量只在0和1之间变动时(互斥量、二元信号量),就可以把信号量看作成一把轻量的互斥锁。但是互斥量实现的互斥锁还是与真正的互斥锁有所区别的,互斥锁只能是哪个线程获取的锁,哪个线程释放;而互斥量可以由不同的线程来acquire和release

        信号量一般不依赖于互斥锁,它可以独自使用,如本文线程通信的导入问题,让任务2先于任务1执行就可以用信号量来解决。但是信号量也可以与互斥锁一起使用来实现生产者消费者模型:

        注: 在使用信号量时,一定要先申请信号量再申请互斥锁。因为互斥锁只能由一个线程持有,如果先申请互斥锁的话,那么除了持有互斥锁的线程,其余的线程就会被阻塞在互斥锁处,而去申请信号量,那么信号量就只会被持有锁的线程申请和释放,信号量就形同虚设了。

        详情可以参考:

        用posix信号量实验环形队列并完成生产消费者模型 

        C++20 semaphore(信号量) 详解 

        总结起来,信号量主要用于控制对共享资源的访问,而条件变量主要用于线程间的通信和协作。它们在不同的场景下有不同的作用和用途。 

这篇关于线程池前置知识之线程同步的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1061767

相关文章

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

Java中常见队列举例详解(非线程安全)

《Java中常见队列举例详解(非线程安全)》队列用于模拟队列这种数据结构,队列通常是指先进先出的容器,:本文主要介绍Java中常见队列(非线程安全)的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一.队列定义 二.常见接口 三.常见实现类3.1 ArrayDeque3.1.1 实现原理3.1.2

SpringBoot3中使用虚拟线程的完整步骤

《SpringBoot3中使用虚拟线程的完整步骤》在SpringBoot3中使用Java21+的虚拟线程(VirtualThreads)可以显著提升I/O密集型应用的并发能力,这篇文章为大家介绍了详细... 目录1. 环境准备2. 配置虚拟线程方式一:全局启用虚拟线程(Tomcat/Jetty)方式二:异步

Mysql的主从同步/复制的原理分析

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录为什么要主从同步?mysql主从同步架构有哪些?Mysql主从复制的原理/整体流程级联复制架构为什么好?Mysql主从复制注意

如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socket read timed out的问题

《如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socketreadtimedout的问题》:本文主要介绍解决Druid线程... 目录异常信息触发场景找到版本发布更新的说明从版本更新信息可以看到该默认逻辑已经去除总结异常信息触发场景复

Mac备忘录怎么导出/备份和云同步? Mac备忘录使用技巧

《Mac备忘录怎么导出/备份和云同步?Mac备忘录使用技巧》备忘录作为iOS里简单而又不可或缺的一个系统应用,上手容易,可以满足我们日常生活中各种记录的需求,今天我们就来看看Mac备忘录的导出、... 「备忘录」是 MAC 上的一款常用应用,它可以帮助我们捕捉灵感、记录待办事项或保存重要信息。为了便于在不同

查看MySql主从同步的偏移量方式

《查看MySql主从同步的偏移量方式》:本文主要介绍查看MySql主从同步的偏移量方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 1.mysql的主从同步方案mysqlphp为了在实现读写分离,主库写,从库读mysql的同步方案主要是通过从库读取主库的binl

MySQL主从同步延迟问题的全面解决方案

《MySQL主从同步延迟问题的全面解决方案》MySQL主从同步延迟是分布式数据库系统中的常见问题,会导致从库读取到过期数据,影响业务一致性,下面我将深入分析延迟原因并提供多层次的解决方案,需要的朋友可... 目录一、同步延迟原因深度分析1.1 主从复制原理回顾1.2 延迟产生的关键环节二、实时监控与诊断方案