摘要:实际上,没有任何语言或操作系统可以为你提供异步突然终止线程的便利,且不会警告你不要使用它们。

本文分享自华为云社区《如何编写高效、优雅、可信代码系列(1)——C++多线程强制终止》,原文作者:我是一颗大西瓜  。

故事的起因来源于我在优化他人c++源码的时候,想通过多线程的方式提升程序的运算效率,主要存在以下需求和难点:

  1. 多个线程并行跑模型,看哪个模型跑的快,跑出来后结束其他线程,线程间独立运行无通信过程
  2. 源码模型很复杂,函数调用较多,不好改动,因此不太适合通过信号或标志进行通信终止

网上搜索了一下线程结束的几种方式:

  1. 线程函数的return返回(建议)。这种退出线程的方式是最安全的,在线程函数return返回后, 会清理函数内申请的类对象, 即调用这些对象的析构函数.。然后会自动调用 _endthreadex()函数来清理 _beginthreadex()函数申请的资源(主要是创建的tiddata对象)。
  2. 同一个进程或另一个进程中的线程调用TerminateThread函数(应避免使用该方法)。TerminateThread能够撤消任何线程,其中hThread参数用于标识被终止运行的线程的句柄。当线程终止运行时,它的退出代码成为你作为dwExitCode参数传递的值。同时,线程的内核对象的使用计数也被递减。注意TerminateThread函数是异步运行的函数,也就是说,它告诉系统你想要线程终止运行,但是,当函数返回时,不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,必须调用WaitForSingleObject或者类似的函数,传递线程的句柄。
  3. 通过调用ExitThread函数,线程将自行撤消(最好不使用该方法)。该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++类对象)将不被析构。
  4. 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 2std::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_pthreadThread 1 thread id 140109390030592Thread 2 thread id 140109381637888Thread 1 native handle 140109390030592Thread 2 native handle 140109381637888Thread 1 pthread_t 140109390030592Thread 2 pthread_t 140109381637888

uncommentpos 1或者pos 2后,即调用join()或之后detach(),C++线程会丢失本机句柄类型的信息

$ ./cpp_thread_pthreadThread 1 pthread_t 139811504355072Thread 2 pthread_t 139811495962368Thread 1 thread id thread::id of a non-executing threadThread 2 thread id thread::id of a non-executing threadThread 1 native handle 0Thread 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 1foo.start_thread(tname1);foo.stop_thread(tname1);// create and kill thread 2foo.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_threadThread test_thread1 created:30332 30333 pts/5    00:00:00 test_thread1Thread test_thread1 killed:Thread test_thread2 created:30332 30340 pts/5    00:00:00 test_thread2Thread test_thread2 killed:

当然,条件允许的话最好还是使用返回或信号的方式终止线程,这样也符合安全可信的要求。

点击关注,第一时间了解华为云新鲜技术~

C++多线程强制终止相关推荐

  1. python强制终止_python强制终止

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 方法1:采用sys.exit(0)正常终止程序,从图中可以看到,程序终止后she ...

  2. HTML文件命名_没有删不掉的文件:强制终止、一键解锁,样样精通

    微信搜一搜麦克NO1日常在使用电脑的时候,经常会遇到这样一个问题:想要删除某文件夹里的文件是,系统弹出:该文件无法删除.被系统占用等.那么,一旦遇到该问题该如何应对呢?以后不再担心,接下来小编为大家分 ...

  3. 多线程的终止方法(停止线程)

    多线程的终止方法 多线程的终止共有3种方法 run方法执行结束,正常退出 异常终止 暴力终止 停止线程 停止线程是多线程中的一个重要技术点,本篇文章将对线程的停止操作进行详细讲解. Thread类中是 ...

  4. oracle强制停止执行,Oracle强制终止在执行的sql

    Oracle强制终止在执行的sql 在一下场景中,sql在javaweb程序中(非pq/sql或者sqlplus)执行时有些sql查询数据量较大导致了一下sql执行起来特别占用资源,致使jvm无法回收 ...

  5. WIN10 查看隐藏进程并强制终止进程

    1.使用管理员身份启动cmd 2.在cmd终端输入"tasklist",即可查看系统所有进程,包括隐藏进程 3.还支持模糊查找进程,输入 "tasklist | find ...

  6. linux用命令强制关闭进程,Linux如何使用kill命令强制终止进程

    Linux如何使用kill命令强制终止进程 在Windows系统中,如果应用程序无反应我们会启动任务管理器终止应用,而在Linux系统中则使用kill命令,kill命令主要用于强制关闭进程,下面小编就 ...

  7. MATLAB —— 强制终止关闭所有 EXCEL 进程

    报错提示:xlsx进程被锁定 [~, ~] =system('taskkill /F /IM EXCEL.EXE'); % Keill all running "EXCEL" pr ...

  8. Linux-C编程 / 多线程 / 如何终止某个线程?

    示例 demo 最简单的 demo: static void* thread1_func(void *arg) {int i = 0;// able to be cancelpthread_setca ...

  9. MATLAB程序强制终止时进度条关不掉怎么办?看这里

    当我们MATLAB程序提前终止时,函数里面创建的进度条,没有跑到delete进度条,退到主函数又找不到进度条创建的句柄,点击进度条的×又关不掉,close all 又没有用,每次都要重启MATLAB, ...

最新文章

  1. beamSearch算法原理
  2. mysql怎么实现事务序列化_MySQL 架构 - 事务处理
  3. VUE.js项目中控制台报错: Uncaught (in promise) NavigationDuplicated解决方法
  4. php申请证书,用phpstudy来申请SSL证书
  5. oracle解析md5,Oracle中的MD5加密详解
  6. python 无法调用turtle_新人求助,关于python 调用turtle《python简单turtle教程》
  7. python矩阵_Python矩阵
  8. 初步学习Django-第八篇:ORM常用操作
  9. LVS 三种工作模式
  10. JAVA设对话框的位置,setLocation/setBounds皆可
  11. 给“大学生IT博客大赛”参赛博主的一封信
  12. QT递归获取指定目录下的所有文件
  13. qt实现网易云音乐播放器的图片旋转功能(paintevent)
  14. Redis单线程模型
  15. android常用快捷键大全,AndroidStudio 快捷键使用总结大全
  16. 云防护当道,如何绕过云防护来进行渗透测试
  17. How DICK’S moved its software development in-house and aced omnichannel retail
  18. PMP-PMBOK-培训(7)Initiating a Project and Preparing the Project Plan
  19. Prometheus pod 流量监控
  20. 10_Mysql查询

热门文章

  1. 吊打面试官 | Java到底是值传递还是引用传递
  2. Bootstrap 状态切换
  3. 一般微型计算机有几十条,计算机单选题.doc
  4. YIi 设置 ajax 验证
  5. L2-DAY 2-程序完善夜
  6. Ubuntu 18.04 上使用 OpenJDK 安装并运行 Tomcat
  7. http请求requestUtils
  8. 2016级算法第六次上机-D.AlvinZH的学霸养成记V
  9. 自定义WPF ListBox的选择样式
  10. git-SSH连接配置