C++多线程强制终止
摘要:实际上,没有任何语言或操作系统可以为你提供异步突然终止线程的便利,且不会警告你不要使用它们。
本文分享自华为云社区《如何编写高效、优雅、可信代码系列(1)——C++多线程强制终止》,原文作者:我是一颗大西瓜 。
故事的起因来源于我在优化他人c++源码的时候,想通过多线程的方式提升程序的运算效率,主要存在以下需求和难点:
- 多个线程并行跑模型,看哪个模型跑的快,跑出来后结束其他线程,线程间独立运行无通信过程
- 源码模型很复杂,函数调用较多,不好改动,因此不太适合通过信号或标志进行通信终止
网上搜索了一下线程结束的几种方式:
- 线程函数的return返回(建议)。这种退出线程的方式是最安全的,在线程函数return返回后, 会清理函数内申请的类对象, 即调用这些对象的析构函数.。然后会自动调用 _endthreadex()函数来清理 _beginthreadex()函数申请的资源(主要是创建的tiddata对象)。
- 同一个进程或另一个进程中的线程调用TerminateThread函数(应避免使用该方法)。TerminateThread能够撤消任何线程,其中hThread参数用于标识被终止运行的线程的句柄。当线程终止运行时,它的退出代码成为你作为dwExitCode参数传递的值。同时,线程的内核对象的使用计数也被递减。注意TerminateThread函数是异步运行的函数,也就是说,它告诉系统你想要线程终止运行,但是,当函数返回时,不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,必须调用WaitForSingleObject或者类似的函数,传递线程的句柄。
- 通过调用ExitThread函数,线程将自行撤消(最好不使用该方法)。该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++类对象)将不被析构。
- ExitProcess和TerminateProcess函数也可以用来终止线程的运行(应避免使用该方法)。
选项2和3可能会导致内存泄漏,实际上,没有任何语言或操作系统可以为你提供异步突然终止线程的便利,且不会警告你不要使用它们。所有这些执行环境都强烈建议开发人员,甚至要求在协作或同步线程终止的基础上构建多线程应用程序。
现有的线程结束函数,包括linux系统的pthread.h中的pthread_exit()和pthread_cancel(),windows系统的win32.h中的ExitThread()和TerminateThread(),也就是说,C++没有提供kill掉某个线程的能力,只能被动地等待某个线程的自然结束,析构函数~thread()也不能停止线程,析构函数只能在线程静止时终止线程joinable,对于连接/分离的线程,析构函数根本无法终止线程。
要终止与OS /编译器相关的函数的线程,我们需要知道如何从C++获取本机线程数据类型std::thread。幸运的是,在调用或之前std::thread提供了一个API native_handle()以获取线程的本机句柄类型。并且可以将此本地句柄传递给本地OS线程终止函数,例如join() detach() pthread_cancel()。
以下代码用于显示std::thread::native_handle(),std::thread::get_id()并pthread_self()返回相同的代码pthread_t来处理Linux / GCC的C++线程
#include <mutex>
#include <iostream>
#include <chrono>
#include <cstring>
#include <pthread.h>
std::mutex iomutex;
void f(int num)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::lock_guard<std::mutex> lk(iomutex);
std::cout << "Thread " << num << " pthread_t " << pthread_self() << std::endl;
}
int main()
{
std::thread t1(f, 1), t2(f, 2);
//t1.join(); t2.join(); ----------------pos 1
//t1.detach(); t2.detach(); -------------pos 2
std::cout << "Thread 1 thread id " << t1.get_id() << std::endl;
std::cout << "Thread 2 thread id " << t2.get_id() << std::endl;
std::cout << "Thread 1 native handle " << t1.native_handle() << std::endl;
std::cout << "Thread 2 native handle " << t2.native_handle() << std::endl;
t1.join(); t2.join();
//t1.detach(); t2.detach();
}
运行后可以得到结果
$ g++ -Wall -std=c++11 cpp_thread_pthread.cc -o cpp_thread_pthread -pthread -lpthread $ ./cpp_thread_pthread Thread 1 thread id 140109390030592 Thread 2 thread id 140109381637888 Thread 1 native handle 140109390030592 Thread 2 native handle 140109381637888 Thread 1 pthread_t 140109390030592 Thread 2 pthread_t 140109381637888
uncommentpos 1或者pos 2后,即调用join()或之后detach(),C++线程会丢失本机句柄类型的信息
$ ./cpp_thread_pthread Thread 1 pthread_t 139811504355072 Thread 2 pthread_t 139811495962368 Thread 1 thread id thread::id of a non-executing thread Thread 2 thread id thread::id of a non-executing thread Thread 1 native handle 0 Thread 2 native handle 0
因此,要有效地调用本机线程终止函数(例如pthread_cancel),需要在调用std::thread::join()时或之前保存本机句柄std::thread::detach()。这样,始终可以使用有效的本机句柄终止线程。
class Foo {
public:
void sleep_for(const std::string &tname, int num)
{
prctl(PR_SET_NAME,tname.c_str(),0,0,0);
sleep(num);
}
void start_thread(const std::string &tname)
{
std::thread thrd = std::thread(&Foo::sleep_for, this, tname, 3600);
tm_[tname] = thrd.native_handle();
thrd.detach();
std::cout << "Thread " << tname << " created:" << std::endl;
}
void stop_thread(const std::string &tname)
{
ThreadMap::const_iterator it = tm_.find(tname);
if (it != tm_.end()) {
pthread_cancel(it->second);
tm_.erase(tname);
std::cout << "Thread " << tname << " killed:" << std::endl;
}
}
private:
typedef std::unordered_map<std::string, pthread_t> ThreadMap;
ThreadMap tm_;
};
int main()
{
Foo foo;
std::string keyword("test_thread");
std::string tname1 = keyword + "1";
std::string tname2 = keyword + "2";
// create and kill thread 1
foo.start_thread(tname1);
foo.stop_thread(tname1);
// create and kill thread 2
foo.start_thread(tname2);
foo.stop_thread(tname2);
return 0;
}
结果是
$ g++ -Wall -std=c++11 kill_cpp_thread.cc -o kill_cpp_thread -pthread -lpthread $ ./kill_cpp_thread Thread test_thread1 created: 30332 30333 pts/5 00:00:00 test_thread1 Thread test_thread1 killed: Thread test_thread2 created: 30332 30340 pts/5 00:00:00 test_thread2 Thread test_thread2 killed:
当然,条件允许的话最好还是使用返回或信号的方式终止线程,这样也符合安全可信的要求。
C++多线程强制终止的更多相关文章
- 如何按名称或PID查找一个进程?如何按端口号查找一个进程?如何查看一个进程的CPU和内存、文件句柄使用情况?如何查看CPU利用率高的TOP10进程清单?如何根据PID强制终止进程?
如何按名称或PID查找一个进程?如何按端口号查找一个进程?如何查看一个进程的CPU和内存.文件句柄使用情况?如何查看CPU利用率高的TOP10进程清单? 目录 如何按名称或PID查找一个进程?如何按端 ...
- Eclispe造成的tomcat占用端口 无法启动 强制终止进程 转载
很多时候运行tomcat 的时候总是会提示tomcat 的端口被占用 但是任务管理器里面还找不到是哪个端口被占用了 因此很多人就重新配置tomcat 或者去修改tomcat的端口号 ,其实这么做太麻 ...
- Python中的多进程与多线程(一)
一.背景 最近在Azkaban的测试工作中,需要在测试环境下模拟线上的调度场景进行稳定性测试.故而重操python旧业,通过python编写脚本来构造类似线上的调度场景.在脚本编写过程中,碰到这样一个 ...
- 第一章 Java多线程技能
1.初步了解"进程"."线程"."多线程" 说到多线程,大多都会联系到"进程"和"线程".那么这两者 ...
- C#多线程学习笔记
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- python 多线程学习
多线程(multithreaded,MT),是指从软件或者硬件上实现多个线程并发执行的技术 什么是进程? 计算机程序只不过是磁盘中可执行的二进制(或其他类型)的数据.它们只有在被读取到内存中,被操作系 ...
- Delphi中线程类TThread实现多线程编程1---构造、析构……
参考:http://www.cnblogs.com/rogee/archive/2010/09/20/1832053.html Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大 ...
- C#中的多线程 - 基础知识
原文:http://www.albahari.com/threading/ 文章来源:http://blog.gkarch.com/threading/part1.html 1简介及概念 C# 支持通 ...
- Java多线程程序设计详细解析
一.理解多线程 多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立. 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线 ...
- Python3 多进程和多线程
Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为 ...
随机推荐
- ExtJS的使用方法汇总(1)——配置和表格控件使用
在网上差一些关于ExtJS的相关资料,看到这篇博客写的不错,拿出来分享一下! 博客文章:ExtJS的使用方法汇总(1)--配置和表格控件使用 ExtJS的使用方法汇总(2)- ...
- 安装k8s
完成安装k8s集群以后,推荐使用https://github.com/512team/dhorse发布应用. 准备环境 准备两台服务器节点,如果需要安装虚拟机,可以参考<wmware和cento ...
- Unity - UIWidgets 2. 控件组合
UIWidgets没有提供完整文档, 称可以去看Flutter的文档 中文 \ 英文 控件(Control)在Flutter中称为"Widget", 一个界面的若干控件是通过wid ...
- JS异步任务的并行、串行,以及二者结合
让多个异步任务按照我们的想法执行,是开发中常见的需求.今天我们就来捋一下,如何让多个异步任务并行,串行,以及并行串行相结合. 一.并行 并行是使用最多的方式,多个相互间没有依赖关系的异步任务,并行执行 ...
- IDEA在Debug模式下修改Java类,不小心关闭 Reload Changed Classes for AppArrowWebApplication 框提示之后的处理
问题描述:Springboot maven 聚合项目里面,经常要启动多个服务. 当我们修改其中一个服务的时候,debug 启动时 idea 就会提示是否需要重新编译修改的内容,弹窗让你选择reload ...
- 如何将word格式的文档转换成markdown格式的文档
如何将word格式的文档转换成markdown格式的文档 如何将word格式的文档转换成markdown格式的文档 前言 A. 介绍Markdown和Word格式文档 什么是Markdown? Mar ...
- 《流畅的Python》 读书笔记 第8章_对象引用、可变性和垃圾回收
第8章_对象引用.可变性和垃圾回收 本章的主题是对象与对象名称之间的区别.名称不是对象,而是单独的东西 name = 'wuxianfeng' # name是对象名称 'wuxianfeng'是个st ...
- DP:打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给定一个代表每 ...
- 信创就用国产的 Solon Java Framework,v2.6.0 发布
先吹牛! 在 v2.6 这个新的里程碑节点,Solon 又完成了几件惊天大事(每一件,都是经历了漫长时间打磨与积累): (1)Solon Native,有了第一个开源案例:dromara/neutri ...
- C# 对象与JSON字符串互相转换
一.JSON字符串转对象(反序列化) 1.使用Newtonsoft.Json 反序列化字符串转换为指定类型 (T) JsonConvert.DeserializeObject<T>(jso ...