此账号为华为云开发者社区官方运营账号,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态
本文分享自华为云社区《如何编写高效、优雅、可信代码系列(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
运行后可以得到结果
$ g++ -Wall -std=c++11 cpp_thread_pthread.cc -o cpp_thread_pthread -pthread -lpthread$ ./cpp_thread_pthread Thread 1 thread id Thread 2 thread id Thread 1 native handle Thread 2 native handle Thread 1 pthread_t Thread 2 pthread_t
uncommentpos 1 或者 pos2 后,即调用 join()或之后 detach(),C++线程会丢失本机句柄类型的信息
$ ./cpp_thread_pthreadThread 1 pthread_t Thread 2 pthread_t Thread 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
结果是
$ g++ -Wall -std=c++11 kill_cpp_thread.cc -o kill_cpp_thread -pthread -lpthread$ ./kill_cpp_thread Thread test_thread1 created: pts/5 00:00:00 test_thread1Thread test_thread1 killed:Thread test_thread2 created: pts/5 00:00:00 test_thread2Thread test_thread2 killed:
当然,条件允许的话最好还是使用返回或信号的方式终止线程,这样也符合安全可信的要求。
点击关注,第一时间了解华为云新鲜技术~
标签: 线程
②文章观点仅代表原作者本人不代表本站立场,并不完全代表本站赞同其观点和对其真实性负责。
③文章版权归原作者所有,部分转载文章仅为传播更多信息、受益服务用户之目的,如信息标记有误,请联系站长修正。
④本站一律禁止以任何方式发布或转载任何违法违规的相关信息,如发现本站上有涉嫌侵权/违规及任何不妥的内容,请第一时间反馈。发送邮件到 88667178@qq.com,经核实立即修正或删除。