乐趣区

关于c++:C多线程强制终止

摘要:实际上,没有任何语言或操作系统能够为你提供异步忽然终止线程的便当,且不会正告你不要应用它们。

本文分享自华为云社区《如何编写高效、优雅、可信代码系列(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 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:

当然,条件容许的话最好还是应用返回或信号的形式终止线程,这样也合乎平安可信的要求。

点击关注,第一工夫理解华为云陈腐技术~

退出移动版