C++11 Thead线程和线程池

参考资料:

2、5.lock_guard 与 std::unique_lock-陈子青的编程学习课堂 (seestudy.cn)

3、C++11 多线程编程-小白零基础到手撕线程池_哔哩哔哩_bilibili

一、 C++11 Thead线程库的基本使用

# include <thread>
std::thread t(function_name, args...);   // 线程开始运行
t.join() // 等待线程完成
t.detach() // 分离线程,让它在后台运行

示例代码一

#include <iostream>
#include <thread>
void print_message() {    
    std::cout << "Hello, world!" << std::endl;
}
int main() {    
    std::thread t(print_message);
    t.join();    
    return 0;
}

示例代码二

#include <iostream>
#include <thread>
#include <functional> // 包含 std::ref
using namespace std;

void increment(int& x) {
    x++; // 对参数进行递增操作
}

int main() {
    int num = 5;
    thread t(std::ref(increment), std::ref(num));  // 可用方式一
    // thread t(increment, std::ref(num));  // 可用方式二
    // thread t(increment, num); 这个会报错!!!
    t.join();
    cout << "After increment: " << num << endl;
    cout << "increment: " << increment << endl;
    return 0;
}

二、 C++11 Thead易错

易错一:多线程使用了局部变量

1、有些运行环境下会出现Aborted (core dumped),原因是a是局部变量,在test作用域内会消失,而在线程内继续引用了a;

2、有些运行环境是不会报错,但是会出现结果不可预测

hq@nuc:~/java/my-project2$ ./a.out 
32766

示例代码

#include <iostream>
#include <thread>
std::thread t;
// int a = 1;  // 正确示例应该将a变成全局区域
void foo(int& x) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    x += 1;
    std::cout << x << std::endl;
}

void test(){
    int a = 1;
    t = std::thread(foo, std::ref(a));
}

int main() {
    test();
    t.join();
    return 0;
}
  return 0;
}

易错二:多线程使用了局部变量

1、有些运行环境下会出现Aborted (core dumped),原因是a是局部变量,在test作用域内会消失,而在线程内继续引用了a;

2、有些运行环境是不会报错,但是会出现结果不可预测

(py37) hq@nuc:~/java/my-project2$ ./a.out 
0
1

示例代码二:

#include <iostream>
#include <thread>
std::thread t;
void foo(int* x) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << *x << std::endl;
    *x += 1;
    std::cout << *x << std::endl;
}

int main() {

    int *a = new int(20);
    t = std::thread(foo, a);
    delete a;
    t.join();
    return 0;
}

正确示例

#include <iostream>
#include <thread>
// std::thread t;
void foo(int* x) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << *x << std::endl;
    *x += 1;
    std::cout << *x << std::endl;
}

int main() {
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
    std::thread t([sharedPtr]() {
        foo(sharedPtr.get());
    });
    std::thread t(foo, sharedPtr.get());
    t.join();
    return 0;
}

线程池

#include <iostream> // 包含标准输入输出流的头文件。
#include <thread> // 包含线程相关的头文件。
#include <vector> // 包含向量容器的头文件。
#include <queue> // 包含队列容器的头文件。
#include <mutex> // 包含互斥量的头文件,用于实现线程安全。
#include <condition_variable> // 包含条件变量的头文件,用于实现线程同步。
#include <functional> // 包含函数对象的头文件,用于传递任务函数。
#include <glog/logging.h>

class ThreadPool { // 定义了一个名为 ThreadPool 的类。
public:
    ThreadPool(size_t numThreads) : stop(false) { // 线程池的构造函数,接受一个参数 numThreads,表示线程池中的线程数量。初始化列表 stop(false) 初始化了成员变量 stop,将其设置为 false,表示线程池初始状态下不处于停止状态。
        for (size_t i = 0; i < numThreads; ++i) { // 使用循环创建指定数量的工作线程。
            workers.emplace_back( // 在工作线程向量中添加一个新的线程,使用 Lambda 表达式初始化线程的执行函数。
                [this] {
                    while (true) { // 工作线程的主循环,保持线程池始终处于运行状态。
                        std::function<void()> task; // 定义了一个函数对象 task,用于存储要执行的任务。
                        {
                            std::unique_lock<std::mutex> lock(queueMutex); // 创建一个互斥锁 lock,用于保护任务队列。
                            condition.wait(lock, [this] { return stop || !tasks.empty(); }); // 等待条件变量,直到满足 stop 或者任务队列不为空的条件。
                            if (stop && tasks.empty()) { return; } // 如果线程池被要求停止并且任务队列为空,则退出线程。
                            task = std::move(tasks.front()); // 从任务队列中获取任务并移动到 task 中。
                            tasks.pop(); // 从任务队列中移除任务。
                        }
                        task(); // 执行任务。
                    }
                }
            );
        }
    }

    template<class F>
    void enqueue(F&& f) { // 定义一个模板函数 enqueue,用于向任务队列中添加任务。
        {
            std::unique_lock<std::mutex> lock(queueMutex); // 创建一个互斥锁 lock,用于保护任务队列。
            tasks.emplace(std::forward<F>(f)); // 将任务添加到任务队列中。
        }
        condition.notify_one(); // 通知一个等待中的线程有新任务可执行。
    }

    ~ThreadPool() { // 线程池的析构函数,用于停止线程池并等待所有线程完成工作。
        {
            std::unique_lock<std::mutex> lock(queueMutex); // 创建一个互斥锁 lock,用于保护任务队列。
            stop = true; // 将停止标志设置为 true,表示线程池将要停止。
        }
        condition.notify_all(); // 通知所有等待中的线程停止。
        for (std::thread& worker : workers) { worker.join(); } // 等待所有工作线程完成工作并退出。
    }

private:
    std::vector<std::thread> workers; // 存储工作线程的向量。
    std::queue<std::function<void()>> tasks; // 存储任务的队列,每个任务都是一个可调用的函数对象。
    std::mutex queueMutex; // 保护任务队列的互斥量。
    std::condition_variable condition; // 用于线程同步的条件变量。
    bool stop; // 表示线程池是否停止的标志。
};

// 示例任务函数
void taskFunction(int taskId) {
    // std::cout << "Task " << taskId << " is running in thread " << std::this_thread::get_id() << std::endl;
    // std::cout << "Task " << taskId << std::endl;
    LOG(INFO) << "Task " << taskId << " is running in thread " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main() {
    ThreadPool pool(10); // 创建一个拥有4个线程的线程池

    // 将一些任务提交到线程池
    for (int i = 0; i < 1000; ++i) {
        pool.enqueue([i] { taskFunction(i); });
    }

    // 主线程等待所有任务完成
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待足够的时间以确保所有任务完成

    return 0;
}

线程数组、任务数组

1、线程数组,是不能关闭的,只能是while(true)

2、线程数组,循环从任务数组中取任务

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

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

相关文章

苍穹外卖学习记录(一)

1.JWT令牌认证 JSON Web Token (JWT)是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任&#xff0c;因为它是数字签名的。 JWT是目前最常用的一种令牌规范&#xff0c;它最…

【学习笔记】Python大数据处理与分析——pandas数据分析

一、pandas中的对象 1、Series对象 由两个相互关联的数组(values, index)组成&#xff0c;前者&#xff08;又称主数组&#xff09;存储数据&#xff0c;后者存储values内每个元素对应关联的标签。 import numpy as np import pandas as pds1 pd.Series([1, 3, 5, 7])print(…

Linux LVM与磁盘配额

目录 一.LVM概述 LVM LVM机制的基本概念 PV&#xff08;Physical Volume&#xff0c;物理卷&#xff09; VG&#xff08;Volume Group&#xff0c;卷组&#xff09; LV&#xff08;Logical Volume&#xff0c;逻辑卷&#xff09; 二.LVM 的管理命令 三.创建并使用LVM …

AutoPSA中推荐用户使用仿CII计算

Q:请问未知错误。优易AUtoPSAInDon3.app1670行是什么原因呢&#xff1f;如下图&#xff1a; A: 这是老的仿GLIF算法&#xff0c;遇到特殊情况有这个提示&#xff0c;建议使用仿CAESARII算法。

车载摄像头视频防抖处理解决方案,全新的稳定视觉体验

面对复杂多变的道路环境和车辆运动状态&#xff0c;如何确保车载摄像头拍摄的视频稳定清晰&#xff0c;一直是行业面临的重要挑战。美摄科技&#xff0c;作为视频防抖技术的领军企业&#xff0c;以其领先的车载摄像头视频防抖处理解决方案&#xff0c;为企业提供了全新的稳定视…

C++ - 文件流fstream

C 文件流是指在C编程中使用的用于文件输入输出操作的机制。这种机制允许程序员以类似于流的方式读取和写入文件数据。在C中&#xff0c;文件流通常使用<fstream>头文件提供的类来实现。 常用的文件流类包括&#xff1a; 1. std::ofstream&#xff1a;用于向文件中写入数…

筑牢个人信息安全防线,海云安受邀参加武汉“名家论坛”国家安全教育日专题讲座

近日&#xff0c;武汉“名家论坛”国家安全教育日专题讲座活动《“刷脸”有风险&#xff0c;如何保护我们的个人信息安全&#xff1f;》在武汉图书馆报告厅举办&#xff0c;海云安副总工程师李博士受邀参加本次活动。 活动以线下讲座、线上直播的形式&#xff0c;结合“普法讲座…

rk3588 安卓调试

rknn装上了android系统&#xff0c;用type-c usb连接上电脑&#xff0c;设备管理器发现了rk3588&#xff0c;但是Android Studio没有发现设备 后来怀疑是驱动没有安装&#xff0c;我用的驱动下载地址&#xff1a; 瑞芯微Rockchip驱动安装助手(适用于RK3308 RK3399等) Mcuzone…

H2O-3机器学习平台源码编译的各种坑

H2O-3机器学习平台是一个非常适合非专业人士学习机器学习的平台&#xff0c;自带WebUI&#xff0c;效果还是蛮不错的&#xff0c;官方也提供了jar包&#xff0c;一条命令就能直接运行&#xff0c;非常方便&#xff0c;但最近有源码编译的需求&#xff0c;实际操作过程中&#x…

error: failed to push some refs to ‘https://gitee.com/zhao-zhimin12/gk.git‘

git push origin master发现以下报错: 解决办法: 一、强制推送 git push origin master -f &#xff08;加上 -f 就是强制&#xff09; 二、 先拉取最新代码&#xff0c;再推送 1.git pull origin master 2.git push origin master

OpenHarmony实战开发-如何使用ArkUIstack 组件实现多层级轮播图。

介绍 本示例介绍使用ArkUIstack 组件实现多层级轮播图。该场景多用于购物、资讯类应用。 效果图预览 使用说明 1.加载完成后显示轮播图可以左右滑动。 实现思路 1.通过stack和offsetx实现多层级堆叠。 Stack() {LazyForEach(this.swiperDataSource, (item: SwiperData, i…

基于51单片机的心形流水灯设计

基于51单片机的心形流水灯 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.采用51单片机做为主控制器&#xff1b; 2.32个彩色&#xff2c;&#xff25;&#xff24;接在单片机的32个双向&#xff29…

机器人码垛机的技术特点与应用

随着科技的飞速发展&#xff0c;机器人技术正逐渐渗透到各个行业领域&#xff0c;其中&#xff0c;机器人码垛机在物流行业的应用尤为引人瞩目。它不仅提高了物流效率&#xff0c;降低了成本&#xff0c;更在改变传统物流模式的同时&#xff0c;为行业发展带来了重大的变革。 一…

通过钉钉发送消息

1、通过钉钉群添加一个机器人 2、代码实现 /*** 发钉钉审核.** param*/private void sendDingDing(PoMaster poMaster){if(poMaster.getTotalPrice().doubleValue() > 500){String url "https://oapi.dingtalk.com/robot/send?access_tokene11bbb694ad4425bf687d2e…

冯喜运:4.17昨日黄金完美区间多空通杀,今日黄金原油分析

【黄金走势分析 】&#xff1a;黄金昨日整体过山车&#xff0c;早盘黄金冲高2392一线后回落&#xff0c;价格在2379-2389区间震荡&#xff0c;午后区间下移&#xff0c;价格在2362-2380继续震荡&#xff0c;晚间价格再次触及2363支撑反弹&#xff0c;连阳上升突破早间高点&…

数据结构速成--栈

由于是速成专题&#xff0c;因此内容不会十分全面&#xff0c;只会涵盖考试重点&#xff0c;各学校课程要求不同 &#xff0c;大家可以按照考纲复习&#xff0c;不全面的内容&#xff0c;可以看一下小编主页数据结构初阶的内容&#xff0c;找到对应专题详细学习一下。 目录 一…

信号量理论

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 理论 信号量是对公共资源的一种预定机制&#xff0c;资源不一定非要持有才算自己的&#xff0c;预定了也算&#xff0c;在未来任意时刻&#xff0c;仍然可以使用。 像我们申请有一块共享内存&#xff0c;如果一个进程正在使…

HTML5 <video> 标签属性、API 方法、事件、自定义样式详解与实用示例

HTML5 <video> 标签为网页内嵌视频提供了强大且便捷的功能。以下是对 <video> 标签的主要属性、API 方法、事件、自定义样式及其使用示例的详细介绍&#xff1a; 一、属性 1. src 定义&#xff1a;指定视频文件的 URL。示例&#xff1a;<video src"my_v…

树莓派驱动开发--iic篇(JY901S陀螺仪的三轴角度简单读取)

前言&#xff1a;既然大家都到了这步&#xff0c;想必对驱动开发有着一定的理解啦吧&#xff01;&#xff01;那我在前面说一下流程&#xff1a; 修改编译设备树》》》编写编译驱动文件》》》编写编译app文件》》》ftp挂载将前面3复制到树莓派的对应位置》》》加载驱动模块》》…

代码随想录训练营Day 24|Python|Leetcode|93.复原IP地址, 78.子集,90.子集II

93.复原IP地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址&#xff0c;但是 &q…
最新文章