多线程-生产者和消费者模式

2023-05-16

1.简单实现多线程

多线程是多任务处理的一种特殊形式,多线程处理允许让一个进程中同时运行两个或两个以上的线程。这样的话,能更加充分发挥计算机的性能,并高效完成用户的任务。

多线程实现的三步骤:

第一步:加入头文件

#include <thread>

第二步:子线程的实现

void worker()	  // 子线程的函数代码 
{
	...
}

第三步:创建线程

std::thread t(worker);
t.join();//或者t.detach();

join和detach的对比:
join()函数是一个等待线程完成函数,主线程需要等待子线程运行结束了才可以往下执行
detach()函数是子线程的分离函数,当调用该函数后,线程就被分离到后台运行,主线程不需要等待该线程结束才往下执行

基于join的多线程

#include <stdio.h>
#include <thread>	//多线程的头文件
using namespace std;
void worker()	  // 子线程的函数代码 
{
    printf("hello thread!\n");
    this_thread::sleep_for(chrono::milliseconds(1000));
}
int main(){
    std::thread t(worker);  // 创建子线程
	t.join();				// 等待子线程结束
    printf("done!\n");
    this_thread::sleep_for(chrono::milliseconds(2000));
    return 0;
}

上面代码的运行结果:显示打印hello thread,然后等待1秒后,在打印done。再等待2秒后,程序结束。

基于detach的多线程

#include <stdio.h>
#include <thread>	//多线程的头文件
using namespace std;
void worker()	  // 子线程的函数代码 
{
    printf("hello thread!\n");
    this_thread::sleep_for(chrono::milliseconds(1000));
}
int main(){
    std::thread t(worker);  // 创建子线程
	t.detach();				// 等待子线程结束
    printf("done!\n");
    this_thread::sleep_for(chrono::milliseconds(2000));
    return 0;
}

上面代码的运行结果:显示打印done,然后打印hello thread。再等待2秒后,程序结束。–此时,main函数和子线程worker的执行是分开执行的,主函数不需要等待worker执行结束才往下执行。

在类中使用多线程

class Infer
{
public:
    Infer()
    {
        worker_thread_ = thread(&Infer::infer_worker,this);
    }
    ~Infer()
    {
        if(worker_thread_.joinable())
            worker_thread_.join();
    }
private:
    void infer_worker()
    {

    }
private:
    thread worker_thread_;
};

2.多线程-锁mutex

在c++的多线程编程中,对于内存中的变量可能出现不同线程的同时读取写,这就应该引入锁来提供“访问保护”。即通过锁实现在一个线程对某个变量进行读写操作的时候,其他线程不能访问该变量。

基于lock和unlock的加锁

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#include <stdexcept>

int counter = 0;
std::mutex mtx; // 保护counter

void increase(int time) {
    for (int i = 0; i < time; i++) {
        mtx.lock();
        // 当前线程休眠1毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        counter++;
        mtx.unlock();
    }
}

int main(int argc, char** argv) {
    std::thread t1(increase, 10000);
    std::thread t2(increase, 10000);
    t1.join();
    t2.join();
    std::cout << "counter:" << counter << std::endl;
    return 0;
}

基于std::lock_guard的加锁

使用lock和unlock的加锁,往往容易忘记加unlock的解锁代码。所以,引入了lock_guard

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#include <stdexcept>

int counter = 0;
std::mutex mtx; // 保护counter

void increase(int time) {
    for (int i = 0; i < time; i++) {
        std::lock_guard<std::mutex> sd(mtx);
        // 当前线程休眠1毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        counter++;
    }
}

int main(int argc, char** argv) {
    std::thread t1(increase, 100);
    std::thread t2(increase, 100);
    t1.join();
    t2.join();
    std::cout << "counter:" << counter << std::endl;
    return 0;
}

死锁

多线程容易出现的常见问题:发生死锁

死锁的例子:线程T1持有锁L1并且申请获得锁L2,而线程T2持有锁L2并且申请获得锁L3,而线程T3持有锁L3并且申请获得锁L1,这样导致了一个锁依赖的环路:T1依赖T2的锁L2,T2依赖T3的锁L3,而T3依赖T1的锁L1。从而导致了死锁。

避免的死锁的方法:
(1).如果出现两个线程,都是先锁锁A,再锁锁B,就能避免死锁的现象.(即不要一个线程,先锁锁A,再锁锁B;另外一个线程,先锁锁B,再锁锁A;这就很容易出现死锁问题)
(2).使用std::lock(锁A, 锁B);进行加锁

3.多线程-condition_variable条件变量

condition_variable类是利用线程间共享的变量进行同步的一种机制,用于阻塞一个线程或者同时阻塞多个线程,直到另一个线程修改共享变量并通知condition_variable.

条件变量的使用总是和一个互斥量结合在一起

条件变量的三步骤:

  1. 锁(锁住共享变量,线程独占),锁使用的是 std::unique_lock< std::mutex >
  2. wait等待(等待通知条件变量,变化的共享变量是否满足条件)
  3. 当notify通知条件变量、超时过期或发生虚假唤醒时,线程被唤醒,互斥锁unlock被原子地重新获取。然后,线程应该检查条件,如果唤醒是假的,则继续等待
#include <iostream>
#include <condition_variable>
using namespace std;
mutex wait_mutex;
condition_variable wait_condition_variable;

// 等待线程函数
void wait_thread_func()
{
	unique_lock<mutex> lock(wait_mutex);
	cout << "等待线程(" << this_thread::get_id() << "): 开始等待通知..." << endl;
	wait_condition_variable.wait(lock); //void wait(std::unique_lock<std::mutex>& lock, Predicate pred); //Predicate 谓词函数,可以普通函数或者lambda表达式
	cout << "等待线程(" << this_thread::get_id() << "): 继续执行代码..." << endl;
}

int main()
{
	thread wait_thread(wait_thread_func);

	this_thread::sleep_for(1s); // 等待1秒后进行通知
	cout << "通知线程(" << this_thread::get_id() << "): 开始通知等待线程..." << endl;
	wait_condition_variable.notify_one();
	wait_thread.join();
	cout << "--- main结束 ---" << endl;
}

在这里插入图片描述

4.多线程-promise

future和promise的作用是在不同线程之间传递数据。使用指针也可以完成数据的传递,但是指针非常危险,因为互斥量不能阻止指针的访问;而且指针的方式传递的数据是固定的,如果更改数据类型,那么还需要更改有关的接口,比价麻烦;promise支持泛型的操作,更加方便编程处理。

#include <iostream>
#include <functional>
#include <future>
#include <thread>
#include <chrono>
#include <cstdlib>

void thread_set_promise(std::promise<int>& promiseObj) {
    std::cout << "In a thread, making data...\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    promiseObj.set_value(35);
    std::cout << "Finished\n";
}

int main() {
    std::promise<int> promiseObj;
    std::future<int> futureObj = promiseObj.get_future();//promise和future绑定,用于获取线程中promiseObj的值
    std::thread t(&thread_set_promise, std::ref(promiseObj));
    std::cout << futureObj.get() << std::endl;// futureObj.get():等待futureObj被赋值,获取futureObj的值
    t.join();
    system("pause");
    return 0;
}

5.生产者和消费者模式概述

概念:多个线程进行生产,同时多个线程进行消费,两种角色通过内存缓冲区进行通信。

即所谓生产者消费者问题实际上主要是包含了两类线程:一类是生产者线程用于生产数据;一类是消费者线程用于消费数据。为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

优点:极大的解决了代码之间的耦合程度
注释:解耦的意思假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多线程-生产者和消费者模式 的相关文章

  • C# 字符串各种操作

    C 字符串 xff1a 字符串 转 char类型的数组字符串 批量合成字符串大小写转换字符串分割字符串替换字符串 是否包含字符串 比较字符串截取字符串 是否 以什么开头 结尾 字符串 第一个 最后一个 字符串 去除空格字符串 空与null
  • C# 里氏转换

    子类可以赋值给父类 子类可以转换为父类 protected 修饰符 是让这个字段 xff0c 在子类中也可以访问 设定访问权限 span class token keyword using span span class token nam
  • Node.js — 内置API模块

    文章目录 0 JS 与 Node js的理解 内置API模块1 导入 fs 模块 xff0c 导入文件系统模块2 导入 path 模块 xff0c 读取文件 xff0c 路径处理模块3 http xff08 创建web服务器的 xff09
  • C# 列表:ArrayList、字典:Hashtable、增删改查

    添加单个 对象或多个 删除指定单个 范围 清空 是否 包含 不包含 索引直接修改 列表 xff1a ArrayList span class token comment 集合 xff08 我称之为列表 xff09 span span cla
  • C# path类:操作路径、File类:操作文件、文件流读写

    路径操作 span class token class name span class token keyword string span span str span class token operator 61 span span cl
  • C# 列表:list 字典:dict

    列表 list 增删改查 与数组转换 span class token comment 创建泛型集合 span span class token class name List span class token punctuation lt
  • C# 虚方法多态、抽象类多态、接口

    C 虚方法多态 抽象类多态 虚方法 xff1a 希望重新父类中的某个方法时 xff0c 使用虚方法 抽象类 xff1a 有多个规定的处理方式 xff0c 但实际实现的方式不同 xff0c 使用抽象类 抽象类就是为了设立规范 xff0c 为了
  • halcon 学习:图像读取与保存、查看类型、图像大小、转为灰度、分割与合并通道、获取图像指针、

    halcon 学习 xff1a 图像读取与保存 查看类型 图像大小 转为灰度 分割与合并通道 获取图像指针 read image span class token punctuation span Image span class toke
  • halcon 获取XLD亚像素的测量距离

    read image span class token punctuation span Image span class token string 39 F xue xi 1 png 39 span span class token pu
  • halcon 基础语法:数组、向量、字典、

    数组 数组 Tuple 1 span class token operator 61 span span class token punctuation span span class token number 1 span span cl
  • halcon 电气柜绿灯位置安装是否正确

    思路 xff1a 找到电气柜的区域根据绿色通道对绿色敏感 xff0c 找到绿色按钮特征过滤 xff0c 与空对象相比 xff0c 确定绿色按钮是否存在填充后截取出来 xff0c 转为亚像素 xff0c 筛选和计算中心点中心点是否在规定区域内
  • C# 基础语法示例

    基础语法 span class token keyword using span span class token namespace System span span class token punctuation span span c
  • C# 委托的基础使用

    委托 我认为委托实际上是一种设计模式的封装 委托的本质就是 xff0c 将大致的流程定下来 xff08 包含输入与输出格式 xff09 xff0c 将其中计算的细节由一个被委托的函数进行具体实现 委托 xff1a 将函数当作形参进行传递 要
  • Node.js — 模块化

    文章目录 1 模块化1 模块化与作用域2 module 模块3 npm与包 1 模块化 1 模块化与作用域 编程领域中的模块化 xff1a 遵守固定的规则 xff0c 把一个大文件拆成独立并相互依赖的多个小模块 模块化好处 xff1a 提高
  • C# 多线程示例

    百度网盘原代码连接 xff1a 链接 xff1a https pan baidu com s 19W3RFOarQtaUQDv L4tmkw 提取码 xff1a q47x span class token keyword using spa
  • C# 读取和写入json文件

    方式一 首先添加引用 导入 using System Web Script Serialization using System IO 需要封装一个类 演示 xff1a 为了方便演示 xff0c 本次使用的是控制台 span class t
  • flask框架学习 git、post请求

    templates 文件夹是放置html文件的 xff0c 否则路径不对会报错 demo1 py 文件内容 span class token keyword from span flask span class token keyword
  • falsk框架 使用post请求,发送与接收json格式的内容(最小的示例)

    falsk框架 使用post请求 xff0c 发送与接收json格式的内容 先运行接收 xff0c 再运行发送 发送 span class token keyword import span requests url span class
  • C# 打包项目

    在线安装 xff1a 打包 xff1a 拓展 在线搜索 xff1a installer 离线安装 xff1a 网址 xff1a https marketplace visualstudio com 搜索输入 xff1a setup inst
  • Windows磁盘管理右键无法删除卷,右键只有帮助选项按钮(转发)

    转发自 xff1a https blog csdn net github 39581355 article details 107670379 Windows磁盘管理右键无法删除卷 xff0c 右键只有帮助选项按钮 问题 xff1a 电脑更

随机推荐

  • SQL语句学习笔记(对库、表、字段、的操作)

    查看mysql的状态 status 启动 停止 mySQL服务 图像界面方法 xff1a dos窗口执行 xff1a services msc 控制面板 gt 管理工具 gt 服务 命令行方法 xff1a 启动 xff1a net star
  • 请求的转发和重定向

    请求的转发和重定向实际上是用来派遣视图页面的 xff0c 不同于ajax xff0c ajax用于页面中数据的的取出和保存 请求的转发 xff1a 用户在页面发送请求到后台 xff0c 如果没有在web xml配置中拦截请求的话 xff0c
  • springmvc-文字解释(无图)

    1 前端控制器 xff08 DisatcherServlet xff09 我们的请求发送到后台 xff0c 通过web xml截取请求 xff0c 通过前端控制器分配该请求 2 处理器映射 xff08 HandlerMapping xff0
  • 在线考试系统

    在线考试系统源码 前端开发语言有 xff1a html xff0c css xff0c javascript xff0c jsp xff0c jstl等 xff0c 前端框架 xff1a jQuery xff0c easyui layui
  • Node.js-- Express

    文章目录 0 学习目标 1 理解express2 基本使用3 路由4 Express中间件1 调用流程 xff1a 2 格式 xff1a 3 next 函数作用 xff1a 4 定义中间件函数5 全局生效的中间件 6 中间件作用 xff1a
  • 数据结构笔记

    一 数据结构是什么 xff1f 数据结构就是已某种特定方式存储数据 xff0c 按某种结构把数据结构化然后存储到内存容器当中 二 我们为什么需要数据结构 xff1f 结构化存储可以让数据有不同的形态 xff0c 我们通过构造多种结构来解决数
  • ES6新特性-含代码-通俗易懂

    一 新增const let变量 const用来定义常量 xff0c 它保存的值是不能再次改变的 这里说的是基本类型 xff0c 如果是对象类型则不可改变其内存地址 可以改变对象中的内容 xff0c 同时也不能多次定义同名变量 const v
  • 考试系统-新版

    最新考试系统 通过JSP xff08 Java Server Page xff09 技术和Tomcat服务器搭建的一个在线考试系统的设计与实现 针对目前的教学考核都普遍存在有选择题 xff0c 题型都是有固定的答案形式 本在线考试系统设计成
  • 金融系统-基金管理

    金融系统 基金管理 本项目为携投基金系统 xff0c 在客户端浏览器输入网址 xff0c 即可载入该系统 xff0c 本系统采用当前主流前端开发语言有 xff1a layui js等前端主流技术 采用的后端开发语言框架等有 xff1a SS
  • 学校教材管理系统-毕设、课设(最佳参考)

    下载地址 高校教材管理系统 项目介绍 基于springboot 43 mybatis 43 jwt 43 layui 43 mybatis 43 html 43 javaScript的用于高校管理教材的系统 项目主要功能 教材信息管理 教材
  • android-校园拍卖管理系统-毕设课设-含源码

    校园拍卖系统 android 源码私信 xff0c 有回必应 xff0c 三连关注 xff0c 免费 xff01 xff01 xff01 android实现校园拍卖系统 xff0c 使用语言为java xff0c 工具idea或者andro
  • ChatGPT 未来的前景以及发展趋势

    当谈到ChatGPT的未来和发展趋势时 xff0c 需要考虑人工智能技术以及文本生成和交互的迅速发展 在这方面 xff0c ChatGPT的前景非常有希望 xff0c 因为它是一种迄今为止最先进的人工智能技术之一 ChatGPT是一种基于机
  • 同步FIFO 两种方法

    RAM 43 空满信号判断 xff0c 两种方法 一 空满标志用指针位置得到 二 空满标志用fifo的中数据的计数得到 一 当写指针超过读指针一圈 xff0c 写满 xff1b 写指针等于读指针 xff0c 读空 96 timescale
  • linux内核串口日志抓取-minicom工具使用方法

    linux抓串口日志 抓串口日志方式minicom保存串口日志log抓取主板串口日志minicom man手册 抓串口日志方式 1 xff09 问题机上 xff0c 找到串口设备 xff0c 比如 dev ttyAMA 0 1 2 3 st
  • 二叉树(七):二叉树的高度与平衡二叉树

    一 二叉树的深度与高度 1 二叉树的深度 对于二叉树中的某个节点 xff0c 其深度是从根节点到该节点的最长简单路径所包含的节点个数 xff0c 是从上面向下面数的 因此访问某个节点的深度要使用先序遍历 2 二叉树的高度 对于二叉树中的某个
  • Python --语法自纠

    文章目录 1 输入2 数据类型转换 xff0c 字符串3 字典 xff0c 列表 xff0c 元组4 语法0 错题 1 输入 输入eval作用一次输入一个或多个 map print format m n format输出 2 数据类型转换
  • 强化学习算法复现(六):DoubleDQN_gym倒立摆

    建立RL brain py span class token keyword import span torch span class token keyword import span torch span class token pun
  • Android的控件绑定----ViewBinding

    在Android开发中 xff0c 控件的绑定是开发者无法绕开的一道程序 是Android开发中最原始 xff0c 也是最基础的一种获取View的方法 在一个复杂布局的页面时 xff0c 我们要一个个控件去调用findViewById方法去
  • C++ OpenCV CV_***未声明的标识符的解决办法

    1 OpenCV cvtColor CV BGR2GRAY未声明的标识符的解决办法 加上这个引用即可 include lt opencv2 imgproc types c h gt 2 opencv里面CV FOURCC找不到标识符 CV
  • 多线程-生产者和消费者模式

    1 简单实现多线程 多线程是多任务处理的一种特殊形式 xff0c 多线程处理允许让一个进程中同时运行两个或两个以上的线程 这样的话 xff0c 能更加充分发挥计算机的性能 xff0c 并高效完成用户的任务 多线程实现的三步骤 xff1a 第