溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++多線程怎樣進行強制終止

發(fā)布時間:2021-09-26 13:43:16 來源:億速云 閱讀:308 作者:柒染 欄目:開發(fā)技術

今天就跟大家聊聊有關C++多線程怎樣進行強制終止,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據(jù)這篇文章可以有所收獲。

    前言:

    故事的起因來源于我在優(yōu)化他人c++源碼的時候,想通過多線程的方式提升程序的運算效率,主要存在以下需求和難點:

    • 多個線程并行跑模型,看哪個模型跑的快,跑出來后結束其他線程,線程間獨立運行無通信過程

    • 源碼模型很復雜,函數(shù)調用較多,不好改動,因此不太適合通過信號或標志進行通信終止

    線程結束的幾種方式:

    線程函數(shù)的return返回(建議):這種退出線程的方式是最安全的,在線程函數(shù)return返回后, 會清理函數(shù)內申請的類對象, 即調用這些對象的析構函數(shù).。然后會自動調用 _endthreadex()函數(shù)來清理 _beginthreadex()函數(shù)申請的資源(主要是創(chuàng)建的tiddata對象)。
    同一個進程或另一個進程中的線程調用TerminateThread函數(shù)(應避免使用該方法):TerminateThread能夠撤消任何線程,其中hThread參數(shù)用于標識被終止運行的線程的句柄。當線程終止運行時,它的退出代碼成為你作為dwExitCode參數(shù)傳遞的值。同時,線程的內核對象的使用計數(shù)也被遞減。注意TerminateThread函數(shù)是異步運行的函數(shù),也就是說,它告訴系統(tǒng)你想要線程終止運行,但是,當函數(shù)返回時,不能保證線程被撤消。如果需要確切地知道該線程已經終止運行,必須調用WaitForSingleObject或者類似的函數(shù),傳遞線程的句柄。
    通過調用ExitThread函數(shù):線程將自行撤消(最好不使用該方法)。該函數(shù)將終止線程的運行,并導致操作系統(tǒng)清除該線程使用的所有操作系統(tǒng)資源。但是,C++資源(如C++類對象)將不被析構。
    ExitProcess和TerminateProcess函數(shù)也可以用來終止線程的運行(應避免使用該方法):
    選項2和3可能會導致內存泄漏,實際上,沒有任何語言或操作系統(tǒng)可以為你提供異步突然終止線程的便利,且不會警告你不要使用它們。所有這些執(zhí)行環(huán)境都強烈建議開發(fā)人員,甚至要求在協(xié)作或同步線程終止的基礎上構建多線程應用程序。

    現(xiàn)有的線程結束函數(shù),包括linux系統(tǒng)的pthread.h中的pthread_exit()pthread_cancel(),windows系統(tǒng)的win32.h中的ExitThread()TerminateThread(),也就是說,C++沒有提供kill掉某個線程的能力,只能被動地等待某個線程的自然結束,析構函數(shù)~thread()也不能停止線程,析構函數(shù)只能在線程靜止時終止線程joinable,對于連接/分離的線程,析構函數(shù)根本無法終止線程。

    要終止與OS /編譯器相關的函數(shù)的線程,我們需要知道如何從C++獲取本機線程數(shù)據(jù)類型std::thread。幸運的是,在調用或之前std::thread提供了一個API native_handle()以獲取線程的本機句柄類型。并且可以將此本地句柄傳遞給本地OS線程終止函數(shù),例如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

    因此,要有效地調用本機線程終止函數(shù)(例如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++多線程怎樣進行強制終止有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業(yè)資訊頻道,感謝大家的支持。

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

    AI