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

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

相关文章

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

详谈redis跟数据库的数据同步问题

《详谈redis跟数据库的数据同步问题》文章讨论了在Redis和数据库数据一致性问题上的解决方案,主要比较了先更新Redis缓存再更新数据库和先更新数据库再更新Redis缓存两种方案,文章指出,删除R... 目录一、Redis 数据库数据一致性的解决方案1.1、更新Redis缓存、删除Redis缓存的区别二

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

Java子线程无法获取Attributes的解决方法(最新推荐)

《Java子线程无法获取Attributes的解决方法(最新推荐)》在Java多线程编程中,子线程无法直接获取主线程设置的Attributes是一个常见问题,本文探讨了这一问题的原因,并提供了两种解决... 目录一、问题原因二、解决方案1. 直接传递数据2. 使用ThreadLocal(适用于线程独立数据)

Nacos集群数据同步方式

《Nacos集群数据同步方式》文章主要介绍了Nacos集群中服务注册信息的同步机制,涉及到负责节点和非负责节点之间的数据同步过程,以及DistroProtocol协议在同步中的应用... 目录引言负责节点(发起同步)DistroProtocolDistroSyncChangeTask获取同步数据getDis

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识