添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
本文为博主原创文章,未经博主允许不得转载,如需转载请先得到博主的同意,如需疑问,请联系stubbornhuang@qq.com,也可以加入计算机图形图像群526867211,以及访问我的个人站点:www.stubbornhuang.com,谢谢。 本文链接: https://blog.csdn.net/HW140701/article/details/114133051 m_Age ++ ; std :: cout << "子线程运行" << std :: endl ; std :: this_thread :: sleep_for ( std :: chrono :: microseconds ( 3000000 ) ) ; void StartThread ( ) if ( m_ChildThreadPtr != nullptr ) m_ChildThreadPtr . reset ( ) ; m_ChildThreadPtr = std :: make_shared < std :: thread > ( & BaseThread :: ChildThreadFunc , this ) ; void StopThread ( ) m_bChildStop = true ; if ( m_ChildThreadPtr != nullptr ) m_ChildThreadPtr - > join ( ) ; //if (m_ChildThreadPtr->joinable()) // m_ChildThreadPtr->join(); private : std :: atomic < int > m_Age ; std :: shared_ptr < std :: thread > m_ChildThreadPtr ; std :: atomic < bool > m_bChildStop ; int main ( ) std :: shared_ptr < BaseThread > pBaseThread = std :: make_shared < BaseThread > ( ) ; if ( pBaseThread != nullptr ) pBaseThread - > StartThread ( ) ; while ( true ) // 在此处填入需要循环的代码 if ( _kbhit ( ) ) // 如果有按键被按下 if ( _getch ( ) == 'q' ) //如果按下了q键则跳出循环 std :: cout << "退出" << std :: endl ; // 不小心执行了两次停止线程 pBaseThread - > StopThread ( ) ; getchar ( ) ; return 0 ;

在上述程序中,我们声明了一个BaseThread类,在该类中我们使用类的成员函数ChildThreadFunc()作为子线程函数,并使用StartThread()开启执行子线程,StopThread()停止执行子线程。

在main()函数中我们先使用StartThread()开启执行子线程,然后再通过按q键调用StopThread()退出子线程。

但是我们一不小心就连续按了两下q键,导致调用了两次StopThread()方法,这个时候程序出现了crash,并出现了如下报错:
在这里插入图片描述

2 问题原因

在上述main函数中,当我们按下了第一次q键的时候,子程序已经调用了join方法,这导致std::thread对象失去了与之相关联的线程对象,所以当我们再按下了一次q键,发现现在的std::thread对象已经不可join,导致了程序发生了中断。

3 std::thread出现不可join的几种情况

std::thread在以下几种情况下是不可join的:

  • 由std::thread默认构造的std::thread对象,也就是没有指定线程函数的std::thread对象是不可join的;
  • 该std::thread对象被std::move操作
  • 该std::thread对象已经执行过std::thread::join()方法或者std::thread::detach()方法,此时std::thread对象是不可join的;

4 使用std::thread::join()/std::thread::detach()方法需要注意的点

4.1 在执行std::thread::join()/std::thread::detach()方法之前最好判断该std::thread对象是可join的

if (m_ChildThreadPtr->joinable())
	m_ChildThreadPtr->join();

即第1节中的bug可以如此修复:

#include <iostream>
#include "conio.h"
#include <memory>
#include <thread>
#include <atomic>
class BaseThread
public:
	typedef std::shared_ptr<BaseThread> ptr;
	BaseThread()
		m_Age = 10;
		m_ChildThreadPtr = nullptr;
		m_bChildStop = false;
	virtual~BaseThread()
		std::cout << "析构" << std::endl;
	void ChildThreadFunc()
		while (!m_bChildStop)
			m_Age++;
			std::cout << "子线程运行" << std::endl;
			std::this_thread::sleep_for(std::chrono::microseconds(3000000));
	void StartThread()
		if (m_ChildThreadPtr != nullptr)
			m_ChildThreadPtr.reset();
		m_ChildThreadPtr = std::make_shared<std::thread>(&BaseThread::ChildThreadFunc, this);
	void StopThread()
		m_bChildStop = true;
		if (m_ChildThreadPtr != nullptr)
			if (m_ChildThreadPtr->joinable())
				m_ChildThreadPtr->join();
private:
	std::atomic<int> m_Age;
	std::shared_ptr<std::thread> m_ChildThreadPtr;
	std::atomic<bool> m_bChildStop;
int main()
	std::shared_ptr<BaseThread> pBaseThread = std::make_shared<BaseThread>();
	if (pBaseThread != nullptr)
		pBaseThread->StartThread();
		while (true)
			// 在此处填入需要循环的代码
			if (_kbhit()) // 如果有按键被按下
				if (_getch() == 'q') //如果按下了q键则跳出循环
					std::cout << "退出" << std::endl;
					pBaseThread->StopThread();
	getchar();
	return 0;

在调用join之前判断joinable,只有在线程对象是可join的情况下再进行join或者detach操作。

4.2 不要忘记对有关联的std::thread对象调用join或者detach方法

这里有关联的指的是已经为该线程指定了线程函数。

例如我们将第一节的程序进行修改,删除按q键退出子程序的代码。

#include <iostream>
#include "conio.h"
#include <memory>
#include <thread>
#include <atomic>
class BaseThread
public:
	typedef std::shared_ptr<BaseThread> ptr;
	BaseThread()
		m_Age = 10;
		m_ChildThreadPtr = nullptr;
		m_bChildStop = false;
	virtual~BaseThread()
		std::cout << "析构" << std::endl;
	void ChildThreadFunc()
		while (!m_bChildStop)
			m_Age++;
			std::cout << "子线程运行" << std::endl;
			std::this_thread::sleep_for(std::chrono::microseconds(3000000));
	void StartThread()
		if (m_ChildThreadPtr != nullptr)
			m_ChildThreadPtr.reset();
		m_ChildThreadPtr = std::make_shared<std::thread>(&BaseThread::ChildThreadFunc, this);
	void StopThread()
		m_bChildStop = true;
		if (m_ChildThreadPtr != nullptr)
			if (m_ChildThreadPtr->joinable())
				m_ChildThreadPtr->join();
private:
	std::atomic<int> m_Age;
	std::shared_ptr<std::thread> m_ChildThreadPtr;
	std::atomic<bool> m_bChildStop;
int main()
	std::shared_ptr<BaseThread> pBaseThread = std::make_shared<BaseThread>();
	if (pBaseThread != nullptr)
		pBaseThread->StartThread();
	getchar();
	return 0;

运行上述程序,程序报错:
在这里插入图片描述

在一个有关联的线程函数的std::thread对象如果没有调用join或者detach方法会在std::thread对象析构的时候中断程序。如果程序发生异常,也必须在异常处理中调用std::thread对象的join或者detach方法。

如果有兴趣,可以访问我的个站:https://www.stubbornhuang.com,获取更多感兴趣的知识。

1 调用std::thread::join()方法等待线程退出时的示例问题程序#include &lt;iostream&gt;#include "conio.h"#include &lt;memory&gt;#include &lt;thread&gt;#include &lt;atomic&gt;class BaseThread{public: typedef std::shared_ptr&lt;BaseThread&gt; ptr; BaseThread() { m_ 使用Express和PostgreSQL开发API的模板 入门 :play_button: 这些说明将为您提供在本地计算机上运行并运行的项目的副本,以进行开发和测试。 有关如何在实时系统上部署项目的注释,请参阅部署。 您需要安装这些软件包 设置在配置.env根据情况的基础上,文件.env.example文件: 开发配置: .env 测试配置: .env.test 生产配置: .env.production 部署方式 :package: 安装依赖项: $ npm i 在docker-compose.yml文件中评论api服务 使用以下命令运行db和adminer(可选)容器: $ docker-compose up -d # -d: detach 运行迁移和播种器: environment可以带来价值:发展| 测试生产 # Migrations
std::threadc++11新引入的线程标准库,通过其可以方便的编写与平台无关的多线程程序,虽然对比针对平台来定制化多线程库会使性能达到最大,但是会丧失了可移植性,这样对比其他的高级语言,可谓是一个不足。终于在c++11承认多线程的标准,可谓可喜可贺!!! 在使用std::thread的时候,对创建的线程有两种操作:等待/分离,也就是join/detach操作。join()操作是在std::thread t(func)后“某个”合适的地方调用,其作用是回收对应创建的线程的资源,避免造成资源的泄露。detach()操作是在std::thread t(func)后马上调用,用于把被创建的线程
--restart on-failure:3 \ --publish 127.0.0.1:6080:6080/tcp \ --privileged --env QEMU_KVM=true \ docker.io/hectormolinero/qemu-reactos:latest 该实例将通过Web浏览器从以下位置提供: QEMU_CPU VM允许使用的核心数(默认为2 )。 QEMU_RAM VM允许使用的内存量(默认为1024M )。 QEMU_DISK_SIZE VM磁盘大小(默认为16G )。 QEMU_DISK_FORMAT VM磁盘格式(默认为qcow2 )。 QEMU_K
C++11引入了函数std::thread join(),用于等待某一线程完成自己的任务。下面就来一步步地深入理解这个函数。 在简单的程序中一般只需要一个线程就可以搞定,也就是主线程: int main() cout << "主线程开始运行\n"; 现在假设我要做一个比较耗时的工作,从一个服务器下载一个视频并进行处理,那么我的代码会变成: int main() cout << "主线程开始运行\n"; download(); // 下
C++中的thread对象通常来说表达了执行的线程(thread of execution),这是一个OS或者平台的概念。 thread一旦创建,就开始执行,并且与创建它的线程有一定的关系。 thread创建后,需要在调用线程中需要指明是join还是detach,否则会发生错误。 1、当thread::join()函数被调用后,调用它的线程会被block,直到线程的执行被完成。基本上,这是一...
thread detach, join 线程有两种状态,joinable或者detachable,pthread默认创建的线程是joinable的,也可以指定atrribute创建成一个detachable的线程。一个线程被创建后,最终一定要调用join或者detach(或者设置成detachable),以保证最后线程的资源会得到回收。 对于一个joinable的线程,join它后要等... (1). 默认构造函数,创建一个空的 thread 执行对象。 (2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。 (3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。.
翻译原文:https://thispointer.com/c11-tutorial/ C++11 线程库介绍 C++11 中新增了多线程 std::thread,相比之前使用较多的是操作系统提供的 POSIX 线程接口,新标准引入了线程库无疑带来了许多便利。 要使用 C++11 多线程,首先 gcc 编译器版本需要大于4.8,并且编译时,需要加上参数 -std=c++11 -lpthread,可见,C++11 的线程是对 POSIX 线程的封装。 C++11 线程创建 在每个 C++ 应用程序中,都有一个默
c++中关于std::threadjoin的思考 std::threadc++11新引入的线程标准库,通过其可以方便的编写与平台无关的多线程程序,虽然对比针对平台来定制化多线程库会使性能达到最大,但是会丧失了可移植性,这样对比其他的高级语言,可谓是一个不足。终于在c++11承认多线程的标准,可谓可喜可贺!!! 在使用std::thread的时候,对创建的线程有两种操作:等待/分离,也就是joi...
这个问题涉及到的知识比较广泛,需要包括网络编程、并发编程等方面的内容。 首先,服务器端需要创建一个监听套接字,并绑定到指定的IP地址和端口上,然后通过调用listen函数将该套接字设置为监听状态。 接着,服务器端需要采用多线程或多进程的方式,对于每个新的客户端连接,都创建一个新的线程或进程来处理,避免单线程或单进程的情况下,一个客户端的阻塞操作会影响到其他客户端的正常访问。 在服务器端线程或进程中,需要通过accept函数接收客户端的连接请求,并获得与该客户端通信的套接字,然后将该套接字交给一个新的线程或进程来处理客户端请求。 对于客户端,需要创建一个套接字,并向服务器端发起连接请求。如果连接成功,则可以通过send和recv函数与服务器端进行数据交换。 下面是一个简单的C++实现: 服务器端代码: ```cpp #include <iostream> #include <cstring> #include <thread> #include <mutex> #include <vector> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> std::mutex mtx; void handle_client(int client_socket) { // 处理客户端请求 char buffer[1024]; while(true) { memset(buffer, 0, sizeof(buffer)); int ret = recv(client_socket, buffer, sizeof(buffer), 0); if(ret <= 0) { break; std::cout << "Receive message from client: " << buffer << std::endl; std::string reply = "Hello, I am server!"; send(client_socket, reply.c_str(), reply.size(), 0); close(client_socket); void start_server(int port) { // 创建监听套接字 int server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(server_socket < 0) { std::cerr << "Create socket failed!" << std::endl; return; // 绑定IP地址和端口 struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(port); int ret = bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)); if(ret < 0) { std::cerr << "Bind socket failed!" << std::endl; close(server_socket); return; // 监听 ret = listen(server_socket, 10); if(ret < 0) { std::cerr << "Listen socket failed!" << std::endl; close(server_socket); return; std::cout << "Server start listening on port " << port << " ..." << std::endl; // 处理客户端请求 while(true) { struct sockaddr_in client_addr; socklen_t client_addrlen = sizeof(client_addr); int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addrlen); if(client_socket < 0) { std::cerr << "Accept socket failed!" << std::endl; continue; std::cout << "Accept client connection from " << inet_ntoa(client_addr.sin_addr) << ":" << ntohs(client_addr.sin_port) << std::endl; std::thread th(handle_client, client_socket); th.detach(); close(server_socket); int main() { int port = 9000; start_server(port); return 0; 客户端代码: ```cpp #include <iostream> #include <cstring> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main() { // 创建套接字 int client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(client_socket < 0) { std::cerr << "Create socket failed!" << std::endl; return -1; // 连接服务器 struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(9000); int ret = connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)); if(ret < 0) { std::cerr << "Connect server failed!" << std::endl; close(client_socket); return -1; std::cout << "Connect server success!" << std::endl; // 发送和接收数据 std::string message = "Hello, I am client!"; send(client_socket, message.c_str(), message.size(), 0); char buffer[1024]; memset(buffer, 0, sizeof(buffer)); ret = recv(client_socket, buffer, sizeof(buffer), 0); std::cout << "Receive message from server: " << buffer << std::endl; // 关闭套接字 close(client_socket); return 0; Mediapipe – 将Mediapipe HolisticTracking封装成动态链接库dll/so,实现在桌面应用中嵌入全身关节点识别、手势识别、抬手放手检测识别功能 Tinwai_Leung: 博主你好,我有个小问题,我是直接是用你编译好的dll文件,请问我可以直接通过这个dll获取检测到的骨骼点的坐标吗?一个或者多个点的坐标。希望博主抽空回答一下,感谢。 Eigen踩坑1:Matrix的transpose(矩阵转置)计算之后不能赋值给自身 王昀322: A = A.transposeInPlace(); A.transposeInPlace(); Alphapose - Windows下Alphapose(Pytorch 1.1+)版本2021最新环境配置步骤以及踩坑说明 Zero_Maple_: 环境变量要重新导入一下,就是PATH和LD_LIBRARY_PATH Mediapipe - Windows10 编译Mediapipe C++版本保姆级教程 Tinwai_Leung: 请问我在使用cmd的时候 出现pacman不是内部指令的问题,这怎么解决?