C++多線程強制終止

摘要:實際上,沒有任何語言或操做系統能夠爲你提供異步忽然終止線程的便利,且不會警告你不要使用它們。

本文分享自華爲雲社區《如何編寫高效、優雅、可信代碼系列(1)——C++多線程強制終止》,原文做者:我是一顆大西瓜 。linux

故事的原由來源於我在優化他人c++源碼的時候,想經過多線程的方式提高程序的運算效率,主要存在如下需求和難點:ios

  1. 多個線程並行跑模型,看哪一個模型跑的快,跑出來後結束其餘線程,線程間獨立運行無通訊過程
  2. 源碼模型很複雜,函數調用較多,很差改動,所以不太適合經過信號或標誌進行通訊終止

網上搜索了一下線程結束的幾種方式:c++

1. 線程函數的return返回(建議)。這種退出線程的方式是最安全的,在線程函數return返回後, 會清理函數內申請的類對象, 即調用這些對象的析構函數.。而後會自動調用 _endthreadex()函數來清理 _beginthreadex()函數申請的資源(主要是建立的tiddata對象)。
2. 同一個進程或另外一個進程中的線程調用TerminateThread函數(應避免使用該方法)。TerminateThread可以撤消任何線程,其中hThread參數用於標識被終止運行的線程的句柄。當線程終止運行時,它的退出代碼成爲你做爲dwExitCode參數傳遞的值。同時,線程的內核對象的使用計數也被遞減。注意TerminateThread函數是異步運行的函數,也就是說,它告訴系統你想要線程終止運行,可是,當函數返回時,不能保證線程被撤消。若是須要確切地知道該線程已經終止運行,必須調用WaitForSingleObject或者相似的函數,傳遞線程的句柄。
3. 經過調用ExitThread函數,線程將自行撤消(最好不使用該方法)。該函數將終止線程的運行,並致使操做系統清除該線程使用的全部操做系統資源。可是,C++資源(如C++類對象)將不被析構。
4. ExitProcess和TerminateProcess函數也能夠用來終止線程的運行(應避免使用該方法)。segmentfault

選項2和3可能會致使內存泄漏,實際上,沒有任何語言或操做系統能夠爲你提供異步忽然終止線程的便利,且不會警告你不要使用它們。全部這些執行環境都強烈建議開發人員,甚至要求在協做或同步線程終止的基礎上構建多線程應用程序。windows

現有的線程結束函數,包括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:

固然,條件容許的話最好仍是使用返回或信號的方式終止線程,這樣也符合安全可信的要求。

點擊關注,第一時間瞭解華爲雲新鮮技術~

相關文章
相關標籤/搜索