【C++】多线程(一):std::thread的使用

这篇文章应我朋友的邀请,写一篇文章介绍下C++多线程。

编译环境准备

首先确定你的编译器支持std的thread,如果不支持,就会出现诸如“thread找不到”的问题。

以下假设你使用 gnu gcc 编译器,因为 MSVC 的我也不太熟悉。

linux

std::thread 在 Linux 上的实现借用了 Linux 的 pthread,因此,编译选项需要加入

-pthread

Windows

如果是 Windows,首先要确保你的 mingw gcc 使用的是 posix 接口。如果是 Win32 接口则不可以使用 std 的 thread,尽管你也能在代码里 include thread 的头文件,但是宏定义会禁止使用头文件里的代码。当然,win32 下也可以实现 C++ 的多线程,只不过有自己的一套代码,这里就不赘述了。

可以通过gcc -v查看自己的mingw gcc用的哪个接口:

Using built-in specs.
COLLECT_GCC=D:\Program Files (x86)\Dev-Cpp\TDM-GCC-64\bin\gcc.exe
COLLECT_LTO_WRAPPER=D:/Program\ Files\ (x86)/Dev-Cpp/TDM-GCC-64/bin/../libexec/gcc/x86_64-w64-mingw32/9.2.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-git-9.2.0/configure --build=x86_64-w64-mingw32 --enable-targets=all --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-libgomp --enable-lto --enable-graphite --enable-cxx-flags=-DWINPTHREAD_STATIC --disable-build-with-cxx --disable-build-poststage1-with-cxx --enable-libstdcxx-debug --enable-threads=posix --enable-version-specific-runtime-libs --enable-fully-dynamic-string --enable-libstdcxx-threads --enable-libstdcxx-time --with-gnu-ld --disable-werror --disable-nls --disable-win32-registry --enable-large-address-aware --disable-rpath --disable-symvers --prefix=/mingw64tdm --with-local-prefix=/mingw64tdm --with-pkgversion=tdm64-1 --with-bugurl=http://tdm-gcc.tdragon.net/bugs
Thread model: posix
gcc version 9.2.0 (tdm64-1)

(我这个其实比较拉胯,因为 MinGW 的官网下载太费劲了,就用 Dev C++ 包含的一个 TDM gcc)

不过看 MinGW 的官网,好像现在最新版本已经不支持 Win32 的接口了,只有 posix 的了。

r36 - 2022-01-19Set the default _WIN32_WINNT to 0x0601 (Windows 7), r35 had it at Windows 10 due to mingw-w64 changesChanged time_t to 64-bit on 32-bit Windows by default, matching MSVC (might require rebuilds of existing binaries)POSIX thread model is now the default (and only) version
r35a - 2021-08-16Using POSIX thread model with mingw-w64 winpthreads

std::thread

写一个最简单的 thread 的用法:

#include <thread>
#include <iostream>using namespace std;int main()
{thread td1([]{ cout << "hello world!1" << endl; });thread td2([]{ cout << "hello world!2" << endl; });td1.join();td2.join();return 0;
}

thread 类最基本的用法就是接受一个函数作为参数,这里使用了 lambda 表达式。注意,线程是在thread对象被定义的时候开始执行的,而不是在调用join函数时才执行的,调用join函数只是阻塞等待线程结束并回收资源。
执行结果
由于两个函数之间是并发执行,因此 th2 和 th1 之间打印的先后顺序是不固定的。

如果想在函数中传递参数,在 thread 的参数列表里传入函数的参数即可。

void print_num(int num)
{for (int i = 0; i < num; i++){cout << "Hello " << i << endl;}
}int main()
{thread tds[10];for (int j = 0; j < 10; j++)tds[j] = thread(print_num, j);for (int j = 0; j < 10; j++)tds[j].join();return 0;
}

这次的打印就不那么有序了,至少应该是一个金字塔形式的打印并没有做到。
执行结果

join 和 detach

main 函数本身就是主线程,而调用 thread 相当于新开了一个线程执行操作,这就涉及到不同线程之间的同步问题了。

前面已经提过,join()意味着主线程需要阻塞来等待子线程结束detach()则代表,子线程的控制权和主线程分离(分离的英文为 detach),主线程可以直接结束,不需要等待子线程。我们依然使用上面的代码,只不过把detach改为join
执行结果
可以看到,主线程执行的速度非常快,三次运行都是子线程还未结束,主线程 main() 就已经结束了。不过不要担心,子线程依然会由系统调度在后台运行,主线程结束并不影响子线程。但这就引出了一个问题,一旦子线程调用了主线程中的某些资源,而主线程已经结束,子线程就会因为无法获取这些资源而崩溃。这部分我们以后会详细说明。

另外,需要注意两点:

  1. 不要对调用过 join/detach 的线程再次调用 join/detach。
    此操作会导致程序终止。

  2. 不要在结束前不调用 join/detach。
    线程的析构函数调用时会检查此线程有没有被调用过 join/detach,否则程序也会终止。因为如果不调用一个 joinable 线程的 join ,则该线程就会成为一个僵尸线程,一直留在内核中。

可以使用 joinable() 检查线程有没有被调用过 join/detach,所以正确的写法应该是如下:

    for (int j = 0; j < 10; j++)if(tds[j].joinable())tds[j].detach();

参数传递

thread() 的源代码如下:

      thread(_Callable&& __f, _Args&&... __args){static_assert( __is_invocable<typename decay<_Callable>::type,typename decay<_Args>::type...>::value,"std::thread arguments must be invocable after conversion to rvalues");#ifdef GTHR_ACTIVE_PROXY// Create a reference to pthread_create, not just the gthr weak symbol.auto __depend = reinterpret_cast<void(*)()>(&pthread_create);
#elseauto __depend = nullptr;
#endif_M_start_thread(_S_make_state(__make_invoker(std::forward<_Callable>(__f),std::forward<_Args>(__args)...)),__depend);}

可以看到,参数的传递使用的是右值引用(这里是C++17的折叠表达式),因此这样的函数参数是会编译失败的:

void print_num(int& num)

这涉及到多线程的设计思想,也就是尽可能只传递变量的副本进来,不希望子线程和主线程共享某些变量。但如果你就是想传引用呢?可以使用 std::ref 以引用方式传入,使用std::cref以 const 引用方式传入。

    thread tds[10];for (int j = 0; j < 10; j++)tds[j] = thread(print_num, ref(j));

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Effective Modern C++(1.顶层const与底层const)

1.顶层const与底层const的定义 const修饰的变量不可以改变&#xff0c;那么他就是顶层const&#xff0c;如&#xff1a; const int a 10; 那么&#xff0c;对于 const int *const p new int(10); 第二个const就是顶层const&#xff0c;因为他修饰的是p&#xff1b;第一个…

学习.NET验证模块FluentValidation的基本用法(续3:ASP.NET Core中的调用方式)

FluentValidation模块支持在ASP.NET Core项目中进行手工或自动验证&#xff0c;主要验证方式包括以下三种&#xff1a;   1&#xff09;手工注册验证类&#xff0c;并在控制器或其它模块中调用验证&#xff1b;   2&#xff09;基于ASP.NET验证管道&#xff08;validation …

Visual Studio 中文注释乱码解决方案

在公司多人开发项目中经常遇到拉到最新代码&#xff0c;发现中文注释都是乱码&#xff0c;很是emjoy..... 这是由于编码格式不匹配造成的&#xff0c;如果你的注释是 UTF-8 编码&#xff0c;而文件编码是 GBK 或者其他编码&#xff0c;那么就会出现乱码现象。一般的解决办法是…

打包SpringBoot 项目为本地应用

使用工具&#xff1a;exe4j、Inno Setup Compiler 步骤&#xff1a; 1&#xff0c;将dll包放入项目根路径下&#xff1b; 2&#xff0c;idea 使用Maven打jar包&#xff1b; 3&#xff0c;使用exe4j 工具进行打包&#xff1b; 打开工具首页不动&#xff08;直接 next&#xff…

leetcode_828_统计子串中的唯一字符

题意&#xff1a;所有子串中单个字符出现的次数和 问题转化&#xff1a;对于串中的每个字符&#xff0c;只包含其一次的所有子串的个数和 关于求只包含某位置字符一次的子串个数 class Solution { public:int uniqueLetterString(string s) {/* ...A...A...A...*/int n s.size…

第二十二章 解读pycocotools的API,目标检测mAP的计算COCO的评价指标(工具)

Pycocotools介绍 为使用户更好地使用 COCO数据集, COCO 提供了各种 API。COCO是一个大型的图像数据集&#xff0c;用于目标检测、分割、人的关键点检测、素材分割和标题生成。这个包提供了Matlab、Python和luaapi&#xff0c;这些api有助于在COCO中加载、解析和可视化注释。 …

【Skynet 入门实战练习】实现网关服务 | 用户代理 | RPC 协议 | 客户端

文章目录 前言网关服务RPC 协议看门狗服务代理服务客户端逻辑梳理 前言 上两章学习了如何搭建一个项目&#xff0c;简单实现了几个基础模块。本章节会实现基本的客户端与服务端的通信&#xff0c;包括网关&#xff08;gate&#xff09;、看门狗&#xff08;watchdog&#xff0…

不适合当老师怎么转岗

作为一名老师&#xff0c;你需要耐心、热情、知识储备丰富&#xff0c;还要有一定的演讲技巧。但有时候&#xff0c;即使具备了这些条件&#xff0c;你仍然可能觉得自己的个性或能力并不适合这个职业。那么&#xff0c;该如何转岗呢&#xff1f;别担心&#xff0c;我们为你提供…

玉渊谭天对电影色彩分析的“蚊香图”复现-python

视频教程链接&#xff1a;https://www.bilibili.com/video/BV1Lu4y1t7FG/ 最终的实现效果如下&#xff1a; 前几天刷抖音刷到了玉渊谭天对于电影抽取画面制作“蚊香图”&#xff0c;相关视频片段如下。 这种制作”蚊香图“的特效当时有点触动到到我&#xff0c;根据色彩来分…

深度学习技巧应用30-深度学习中的GPU的基本架构原理与应用技巧

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用30-深度学习中的GPU的基本架构原理与应用技巧,GPU是一种专门用于处理大量并行操作的硬件设备,它的架构设计主要是为了图形渲染。然而,由于其并行处理能力,现在广泛应用于深度学习、科学计算等领域。主要的GPU制造商…

autojs-练手-简单的视频号点赞(初阶版)

注释很详细&#xff0c;直接上代码&#xff08;简单的练手实践&#xff0c;仅供参考&#xff09; //设置点赞次数 var num50; //等待权限授予 auto.waitFor(); //进入点赞流程 while(num!0) {//先向下滑一个视频scrollDown();//使用auto.js找到点赞控件的id&#xff08;每个人不…

《软件方法》2023版第1章:1.1 利润=需求-设计,1.2 ABCD工作流

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 第1章 建模和UML 牵着你走进傍晚的风里&#xff0c;看见万家灯火下面平凡的秘密。 《情歌唱晚》&#xff1b;词&#xff1a;黄群&#xff0c;曲&#xff1a;黄群&#xff0c;唱&#…

复数的几何意义

1、复平面&#xff0c;复数的其它表示法 (1)几何表示法 直角平面坐标&#xff1a; 复平面 实轴&#xff0c;虚轴 (2)向量表示法 向量 模&#xff1a; 复数加减法可用向量的三角形法则或者平行四边形法则 (3)结论 (两边之和大于第三边) ((两边之差大于第三边)) *辐角&am…

【Web】/proc利用相关例题wp

先贴一篇文章一起学习一下 [CTF]proc目录的应用 - CodeAntenna ①[HDCTF 2023]YamiYami 点击Read somethings直接跳转到了百度 从url中发现存在任意文件读取&#xff0c;因为不知道flag在哪&#xff0c;所以考虑读环境变量 payload: ?urlfile:///proc/1/environ 拿到fla…

【Spring源码】Spring Event事件

目录 1、前言 2、什么是Spring Event&#xff1f; 3、基本使用 3.1、定义事件 3.2、发布事件 3.3、监听事件 3.3.1、继承ApplicationListener 3.3.2、使用EventListener注解 4、Spring Event是同步还是异步&#xff1f; 4.1、源码实现 4.2、如何实现异步 4.2.1、使用…

Redis与Mysql的数据强一致性方案

目的 Redis和Msql来保持数据同步&#xff0c;并且强一致&#xff0c;以此来提高对应接口的响应速度&#xff0c;刚开始考虑是用mybatis的二级缓存&#xff0c;发现坑不少&#xff0c;于是决定自己搞 要关注的问题点 操作数据必须是唯一索引 如果更新数据不是唯一索引&#…

5种主流API网关技术选型,yyds!

API网关是微服务项目的重要组成部分&#xff0c;今天来聊聊API网关的技术选型&#xff0c;有理论&#xff0c;有实战。 不 BB&#xff0c;上文章目录&#xff1a; 1 API网关基础 1.1 什么是API网关 API网关是一个服务器&#xff0c;是系统的唯一入口。 从面向对象设计的角度…

docker介绍、部署与常用命令

一、docker 介绍 1、容器&#xff08;Container&#xff09;&#xff1a; (1) 概念&#xff1a; 容器是一种用于运行和部署应用程序的技术。它将应用程序及其所有依赖项&#xff08;例如代码、运行时、系统工具、系统库等&#xff09;打包在一个独立的、可移植的运行环境中&…

Facebook的特点优势

Facebook作为全球最大的社交媒体平台之一&#xff0c;同时也是最受欢迎的社交网站之一&#xff0c;Facebook具有许多独特的特点和优势。本文小编将说一些关于Facebook的特点及优势。 1、全球化 Facebook拥有数十亿的全球用户&#xff0c;覆盖了几乎所有国家和地区。这使得人们…

【深度学习笔记】05 线性回归

线性回归 线性回归基于几个简单的假设&#xff1a; 首先&#xff0c;假设自变量 x \mathbf{x} x和因变量 y y y之间的关系是线性的&#xff0c; 即 y y y可以表示为 x \mathbf{x} x中元素的加权和&#xff0c;这里通常允许包含观测值的一些噪声&#xff1b; 其次&#xff0c;我…