当前位置: 首页 > news >正文

C++11 简单手撕多线程编程

如何使用线程库

std::thread 创建线程
thread1.join(); 阻塞主线程
thread1.detach(); 线程分离

#include<iostream>
#include<thread>void helloworld(std::string msg) {for (int i = 0; i < 10000; i++){std::cout << i << std::endl;}//std::cout << msg << std::endl;return;
}int main() {//1. 创建线程std::thread thread1(helloworld, "hello Tread");//thread1.join();//thread1.detach();bool isJoin = thread1.joinable();if (isJoin) {thread1.join();};std::cout << "over" << std::endl;
}

线程中常见的数据传递错误

临时变量

#include <iostream>
#include <thread>
void foo(int& x) {x += 1;
}
int main() {int x = 1; // 将变量复制到一个持久的对象中std::thread t(foo, std::ref(x)); // 将变量的引用传递给线程,不可以传递临时变量t.join();return 0;
}

传递指针,需要将指针或引用指向堆上的变量,或使用std::shared_ptr等智能指针来管理对象的生命周期。

#include <iostream>
#include <thread>void foo(int* ptr) {std::cout << *ptr << std::endl;delete ptr; // 在使用完指针后,需要手动释放内存
}
int main() {int* ptr = new int(1); // 在堆上分配一个整数变量std::thread t(foo, ptr); // 将指针传递给线程t.join();return 0;
}
#include <iostream>
#include <thread>
#include <memory> // 引入shared_ptr的头文件void foo(std::shared_ptr<int> ptr) {std::cout << *ptr << std::endl;
}int main() {std::shared_ptr<int> ptr = std::make_shared<int>(1); // 使用make_shared分配内存std::thread t(foo, ptr); // 传递shared_ptr到线程t.join();return 0;
}

传递指针或引用指向已释放的内存的问题

#include <iostream>
#include <thread>void foo(int& x) {std::cout << x << std::endl;
}
int main() {int x = 1;std::thread t(foo, std::ref(x)); // 将变量的引用传递给线程,在线程函数执行期间,变量`x`的生命周期是有效的。t.join();return 0;
}

类对象被提前释放

#include <iostream>
#include <thread>
#include <memory>class MyClass {
public:void func() {std::cout << "Thread " << std::this_thread::get_id() << " started" << std::endl;std::cout << "Thread " << std::this_thread::get_id() << " finished" << std::endl;}
};int main() {std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();std::thread t(&MyClass::func, obj);//避免obj被提前销毁,导致未定义的行为t.join();return 0;
}

入口函数为类的私有成员函数

#include <iostream>
#include <thread>class MyClass {
private:friend void myThreadFunc(MyClass* obj);void privateFunc(){std::cout << "Thread " << std::this_thread::get_id() << " privateFunc" << std::endl;}
};void myThreadFunc(MyClass* obj) {obj->privateFunc();
}int main() {MyClass obj;std::thread thread_1(myThreadFunc, &obj);thread_1.join();return 0;
}

将 myThreadFunc 定义为 MyClass 类的友元函数,并在函数中调用 privateFunc 函数。在创建线程时,需要将类对象的指针作为参数传递给线程。

多线程数据共享

互斥锁 (mutex)

#include <iostream>
#include <thread>
#include <mutex>int shared_data = 0;
std::mutex mtx;void func(int n) {for (int i = 0; i < 1000; ++i) {mtx.lock();shared_data++;std::cout << "Thread " << n << " increment shared_data to " << shared_data << std::endl;mtx.unlock();}
}
int main() {std::thread t1(func, 1);std::thread t2(func, 2);t1.join();t2.join();std::cout << "Final shared_data = " << shared_data << std::endl;return 0;
}

互斥量死锁

如果 T1 获取了 mtx1 的所有权,但是无法获取 mtx2 的所有权,而 T2 获取了 mtx2 的所有权,但是无法获取 mtx1 的所有权,两个线程互相等待对方释放互斥量,就会导致死锁。解决办法,需要资源顺序化,或者通过一次性尝试锁定所有指定的互斥锁,确保要么成功获取所有锁,要么释放已持有的锁并重新尝试,从而避免了死锁。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx1, mtx2;void func1() {// 锁定 mtx2,然后再锁定 mtx1mtx2.lock();std::cout << "Thread 1 locked mutex 2" << std::endl;mtx1.lock();std::cout << "Thread 1 locked mutex 1" << std::endl;// 解锁 mtx1 和 mtx2mtx1.unlock();std::cout << "Thread 1 unlocked mutex 1" << std::endl;mtx2.unlock();std::cout << "Thread 1 unlocked mutex 2" << std::endl;
}void func2() {// 锁定 mtx2,然后再锁定 mtx1mtx2.lock();std::cout << "Thread 2 locked mutex 2" << std::endl;mtx1.lock();std::cout << "Thread 2 locked mutex 1" << std::endl;// 解锁 mtx1 和 mtx2mtx1.unlock();std::cout << "Thread 2 unlocked mutex 1" << std::endl;mtx2.unlock();std::cout << "Thread 2 unlocked mutex 2" << std::endl;
}int main() {// 启动两个线程,分别执行 func1 和 func2std::thread t1(func1);std::thread t2(func2);// 等待两个线程结束t1.join();t2.join();return 0;
}

lock_guard 与 std::unique_lock

使用 std::lock 和 std::lock_guard 或 std::unique_lock,它们能确保同时尝试锁定多个互斥量,并且不会死锁。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx1, mtx2;void func1() {std::lock(mtx1, mtx2); // 同时锁定两个互斥量,避免死锁std::lock_guard<std::mutex> lk1(mtx1, std::adopt_lock); // adopt_lock表示互斥量已经被锁定std::lock_guard<std::mutex> lk2(mtx2, std::adopt_lock);std::cout << "Thread 1 locked mutexes 1 and 2" << std::endl;// 自动解锁
}void func2() {std::lock(mtx1, mtx2); // 同时锁定两个互斥量std::lock_guard<std::mutex> lk1(mtx1, std::adopt_lock);std::lock_guard<std::mutex> lk2(mtx2, std::adopt_lock);std::cout << "Thread 2 locked mutexes 1 and 2" << std::endl;// 自动解锁
}int main() {std::thread t1(func1);std::thread t2(func2);t1.join();t2.join();return 0;
}

std::unique_lock 提供了以下几个成员函数:

  • lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁。
  • try_lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则函数立即返回 false,否则返回 true。
  • try_lock_for(const std::chrono::duration<Rep, Period>& rel_time):尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间。
  • try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time):尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间点。
  • unlock():对互斥量进行解锁操作。

call_once

单例设计模式是一种常见的设计模式,用于确保某个类只能创建一个实例。由于单例实例是全局唯一的,因此在多线程环境中使用单例模式时,需要考虑线程安全的问题。
全局只需要一个该类的对象,
下面是一个简单的单例模式的实现:

#include<iostream>
#include<thread>
#include<mutex>
#include<string>class Log {Log() {};
public:Log(const Log& log) = delete;Log& operator = (const Log &log) = delete;static Log& GetInstance() {//static Log log;//return log;static Log *log = nullptr;if (!log) log = new Log;return *log;}void PrintLog(std::string msg) {std::cout<<__TIME__<< msg << std::endl;};
};int main() {Log::GetInstance().PrintLog("error");
}

使用 std::call_once 可以确保单例实例只会被创建一次,从而避免了多个对象被创建的问题。此外,使用 std::unique_ptr 可以确保单例实例被正确地释放,避免了内存泄漏的问题。

#include<iostream>
#include<string>
#include <thread>
#include <mutex>
class Log
{
public:static Log& GetInstance(){//static Log log; //饿汉模式//return log;std::call_once(once, initfunc);return *log;}static void initfunc(){if (!log){log = new Log;}}void PrintLog(std::string msg){std::cout << __TIME__ << " " << msg << std::endl;}private:Log() {}~Log() {}Log(const Log&) = delete;Log& operator=(const Log&) = delete;//静态成员必须类外初始化static Log* log;static std::once_flag once;
};Log* Log::log = nullptr;std::once_flag Log::once; void print_error()
{Log::GetInstance().PrintLog("error");
}int main()
{std::thread t1(print_error);std::thread t2(print_error);t1.join();t2.join();return 0;
}

condition_variable

生产者与消费者模型

跨平台线程池

异步并发

原子操作

相关文章:

C++11 简单手撕多线程编程

如何使用线程库 std::thread 创建线程 thread1.join(); 阻塞主线程 thread1.detach(); 线程分离 #include<iostream> #include<thread>void helloworld(std::string msg) {for (int i 0; i < 10000; i){std::cout << i << std::endl;}//std::cou…...

刷c语言练习题7(牛客网)

1、函数fun的声明为int fun(int *p[4]),以下哪个变量可以作为fun的合法参数&#xff08;&#xff09; A、int a[4][4]; B、int **a; C、int **a[4] D、int (*a)[4]; 答案&#xff1a;B 解析&#xff1a;如果是fun的合法参数&#xff0c;那么其类型应该与定义函数fun中的参数类型…...

Web Worker和WebSocket

Web Worker和WebSocket协议都是Web开发中用于处理多线程和实时通信的技术&#xff0c;但它们的应用场景和工作原理有所不同。 Web Worker Web Worker是HTML5引入的一项技术&#xff0c;它允许JavaScript代码在后台线程中运行&#xff0c;从而实现真正的多线程处理。Web Worke…...

【LeetCode】动态规划—712. 两个字符串的最小ASCII删除和(附完整Python/C++代码)

动态规划—712. 两个字符串的最小ASCII删除和 前言题目描述基本思路1. 问题定义2. 理解问题和递推关系3. 解决方法3.1 动态规划方法3.2 空间优化的动态规划 4. 进一步优化5. 小总结 代码实现PythonPython3代码实现Python 代码解释 CC代码实现C 代码解释 总结: 前言 在字符串处…...

wordpress Contact Form 7插件提交留言时发生错误可能的原因

WordPress Contact Form 7 插件提交留言时发生错误可能有以下几种原因&#xff0c;并提供相应的解决方案&#xff1a; 1. 表单字段验证失败 原因&#xff1a; 用户输入的数据未通过表单字段的验证规则。 解决方案&#xff1a; – 检查表单字段的验证规则是否设置正确。 –…...

uibot发送邮件:自动化邮件发送教程详解!

uibot发送邮件的操作指南&#xff1f;uibot发送邮件的两种方式&#xff1f; 在现代办公环境中&#xff0c;自动化流程的引入极大地提高了工作效率。uibot发送邮件功能成为了许多企业和个人实现邮件自动化发送的首选工具。AokSend将详细介绍如何使用uibot发送邮件。 uibot发送…...

【PostgreSQL】PG数据库表“膨胀”粗浅学习

文章目录 1 为什么需要关注表膨胀&#xff1f;2 如何确定是否发生了表膨胀&#xff1f;2.1 通过查询表的死亡元组占比情况来判断膨胀率2.1.1 指定数据库和表名2.1.2 查询数据库里面所有表的膨胀情况 3 膨胀的原理3.1 什么是膨胀&#xff1f;膨胀率&#xff1f;3.2 哪些数据库元…...

力扣(leetcode)每日一题 871 最低加油次数 | 贪心

871. 最低加油次数 题干 汽车从起点出发驶向目的地&#xff0c;该目的地位于出发位置东面 target 英里处。 沿途有加油站&#xff0c;用数组 stations 表示。其中 stations[i] [positioni, fueli] 表示第 i 个加油站位于出发位置东面 positioni 英里处&#xff0c;并且有 f…...

ppt压缩文件怎么压缩?压缩PPT文件的多种压缩方法

ppt压缩文件怎么压缩&#xff1f;当文件体积过大时&#xff0c;分享和传输就会变得困难。许多电子邮件服务对附件的大小有限制&#xff0c;而在网络环境不佳时&#xff0c;上传和下载大文件可能耗时较长。此外&#xff0c;在不同设备上播放时&#xff0c;较大的PPT文件还可能导…...

2024.10月11日--- SpringMVC拦截器

拦截器 1 回顾过滤器&#xff1a; Servlet规范中的三大接口&#xff1a;Servlet接口&#xff0c;Filter接口、Listener接口。 过滤器接口&#xff0c;是Servlet2.3版本以来&#xff0c;定义的一种小型的&#xff0c;可插拔的Web组件&#xff0c;可以用来拦截和处理Servlet容…...

uniapp 锁屏显示插件 Ba-LockShow(可让vue直接具备锁屏显示能力)

简介 Ba-LockShow 是一款可以直接使uniapp的vue界面在锁屏页展示的插件。 支持使vue直接具备锁屏显示能力支持设置锁屏显示和不显示支持唤醒屏幕 截图展示&#xff08;仅参考&#xff09; 支持定制、本地包、源码等&#xff0c;有建议和需要&#xff0c;请点击文章结尾“Unia…...

CSS计数器

CSS 中的计数器类似于变量&#xff0c;可以实现简单的计数功能&#xff0c;并将结果显示在页面上&#xff0c;在早期的网站上应用比较广泛。要实现计数器需要用到以下几个属性&#xff1a; counter-reset&#xff1a;创建或者重置计数器&#xff1b;counter-increment&#xf…...

嵌入式Linux:信号集

目录 1、信号集初始化 2、向信号集中添加或删除信号 3、测试信号是否在信号集中 在 Linux 系统中&#xff0c;处理多个信号时常用到一种数据结构&#xff1a;信号集&#xff08;sigset_t&#xff09;。信号集允许我们将多个信号组织在一起&#xff0c;以便在系统调用中传递和…...

Linux 外设驱动 应用 1 IO口输出

从这里开始外设驱动介绍&#xff0c;这里使用的IMX8的芯片作为驱动介绍 开发流程&#xff1a; 修改设备树&#xff0c;配置 GPIO1_IO07 为 GPIO 输出。使用 sysfs 接口或编写驱动程序控制 GPIO 引脚。编译并测试。 这里假设设备树&#xff0c;已经配置好了。不在论述这个问题…...

基于SpringBoot+Vue+MySQL的留守儿童爱心网站

系统展示 用户前台界面 管理员后台界面 系统背景 随着现代社会的发展&#xff0c;留守儿童问题日益受到关注。传统的纸质管理方式已经无法满足现代人们对留守儿童爱心信息的需求。为了提高留守儿童爱心信息的管理效率&#xff0c;增加用户信息的安全性&#xff0c;并方便及时反…...

调用第三方接口

目录 一、分析给出的接口文档 二、请求体格式之间的区别 三、示例代码 一、分析给出的接口文档 一般的接口文档包括以下几大部分&#xff1a; 1、请求URL&#xff1a;http://{ip}:{port}/api/ec/dev/message/sendCustomMessageSingle 2、请求方式&#xff1a;POST、GET等 3、…...

JAVA 多线程入门例子:CountDownLatch

首先确定线程数量。如果数据集合的大小小于50&#xff0c;就只使用一个线程&#xff1b;否则使用5个线程。计算每个线程平均处理的数据数量sizePerThread以及余数remainder。在划分数据子集合时&#xff0c;对于每个线程的处理范围进行计算。如果有余数&#xff0c;就将余数依次…...

k8s jenkins 动态创建slave

k8s jenkins 动态创建slave 简述使用jenkins动态slave的优势&#xff1a;配置jenkins动态slave配置 Pod Template配置容器模板挂载卷 测试 简述 持续构建与发布是我们日常工作中必不可少的一个步骤&#xff0c;目前大多公司都采用 Jenkins 集群来搭建符合需求的 CI/CD 流程&am…...

MVS海康工业相机达不到标称最大帧率

文章目录 一、相机参数设置1、取消相机帧率限制2、修改相机图像格式3、调整相机曝光时间4、检查相机数据包大小&#xff08;网口相机特有参数&#xff09;5、 恢复相机默认参数6、 相机 ADC 输出位深调整 二、系统环境设置1、 网口相机设置2、 USB 相机设置 一、相机参数设置 …...

数据结构:用双栈实现一个队列

要用两个栈实现一个队列&#xff0c;可以利用“栈”的后进先出 (LIFO) 特性来模拟“队列”的先进先出 (FIFO) 操作。具体做法是使用两个栈&#xff1a;一个作为入栈栈&#xff0c;另一个作为出栈栈。 算法步骤 入队操作&#xff08;enqueue&#xff09;&#xff1a; 将元素压…...

QScroller Class

Header:#include < QScroller > qmake:QT += widgets Since:Qt 5.0 Inherits:QObject This class was introduced in Qt 5.0. Public Types enum Input {InputPress, InputMove, InputRelease } enum ScrollerGestureType {TouchGesture, LeftMouseButtonGesture,…...

React高阶组件详解

React高阶组件&#xff08;HOC&#xff09;详解 定义 React高阶组件&#xff08;HOC&#xff09;是一个函数&#xff0c;该函数接受一个组件作为参数并返回一个新的组件。高阶组件本身不是一个组件&#xff0c;而是一个函数&#xff0c;它利用React的组合特性&#xff0c;对传入…...

TextView把其它控件挤出屏幕的处理办法

1.如果TextView后面的控件是紧挨着TextView的&#xff0c;可以给TextView添加maxWidth限制其最大长度 上有问题的布局代码 <?xml version"1.0" encoding"utf-8"?> <layout xmlns:android"http://schemas.android.com/apk/res/android&qu…...

长度为 K 的重复字符子串数目

长度为 K 的重复字符子串 给你一个由小写字母组成的长度为n的字符串 S &#xff0c;找出所有长度为 k 且包含重复字符的子串&#xff0c;请你返回全部满足要求的子串的数目。 数据范围&#xff1a; 2≤k≤400 , 5≤n≤900 进阶&#xff1a; 时间复杂度O(n)&#xff0c;空间复杂…...

html+css+js实现轮播图

实现效果&#xff1a; HTML部分 <div class"carousel"><div class"carousel-wrapper"><img src"./image/1.png" alt""></div><ul class"carousel-indictor"><li class"active"…...

Boost集成模型异同

一、常见Boost集成模型 AdaBoost、GBDT和XGBoost都是集成学习中的提升&#xff08;Boosting&#xff09;算法&#xff0c;它们通过组合多个弱学习器来构建一个强学习器。从经验上来说&#xff0c;XGBoost是诸多竞赛的大杀器&#xff0c;在实际业务工作中可能需要用到集成模型的…...

【系统架构设计师】案例专题四:嵌入式系统考点梳理

更多内容请见: 备考系统架构设计师-核心总结目录 摘要:本文主要梳理系统架构设计师 - 嵌入式系统 案例考点 ,主要包括嵌入式相关概念、软件和硬件可靠性、冗余技术、软件容错、集群技术、负载均衡、可维护性的评价指标、软件维护的分类等。 文章目录 一、相关概念二、软件可…...

Ngin入门套餐

快速了解Nginx 一、代理1.1 正向代理1.2 反向代理1.3 正向代理和反向代理的区别 二、Nginx负载均衡策略2.1 轮询&#xff08;Round Robin&#xff09;2.2 加权轮询&#xff08;Weighted Round Robin&#xff09;2.3 IP 哈希&#xff08;IP Hash&#xff09;2.4 最少连接&#x…...

使用linux编译main.cpp文件

1、首先创建一个简单的test.cpp&#xff0c;使用终端命令形式&#xff1a; touch test.cpp 创建结束&#xff0c;记得ls一下&#xff0c;如下&#xff1a; 2、找到创建结束的test.cpp文件&#xff0c;然后右键编辑&#xff0c;输入一个简单的代码&#xff0c;如下 #include …...

服务器部署‌Traefik 实现子级域名路由服务(对外子域名80,路由对内大端口)

文章目录 1.‌Traefik安装2.启动nginx配置路由 本文档只是为了留档方便以后工作运维&#xff0c;或者给同事分享文档内容比较简陋命令也不是特别全&#xff0c;不适合小白观看&#xff0c;如有不懂可以私信&#xff0c;上班期间都是在得 前言&#xff0c;领导让我调研在线发布得…...