boost--線程


 1、thread的使用

  boost的thread包含了線程創建、使用、同步等內容,使用thread需要包含頭文件"boost\thread.hpp"。

  thread中使用了需要編譯的thread庫,所以還需要添加thread庫到項目附加庫目錄,在linux下鏈接thread庫時還需要使用-lpthread選項來鏈接posix線程庫。

  定義一個thread對象后,線程就開始執行。thread構造函數的第一個參數是一個函數或函數對象或function對象,剩余參數是傳遞給執行函數或函數對象的參數, 如果希望是引用傳遞的話則需要配合使用ref。

  成員函數join()和timed_join()可以用來阻塞等待線程執行結束,其中timed_join()可以指定等待時間。成員函數detach()的功能類似於posix的pthread_detach():創建一個線程后默認的狀態是joinable, 如果一個線程結束運行但沒有被join,會有一部分資源沒有被回收。可以在創建線程后調用detach()將線程執行體分離,這樣該線程運行結束后會自動釋放所有資源,而不必使用join來阻塞等待線程結束。調用detach()將線程執行體分離后,成員函數joinable()會返回false,即線程的狀態是非狀態是非joinable。

  需要注意的是thread對象時不可拷貝的。

#include "boost\thread.hpp"

void PrintThreadFunc(const int& n, const string& str)
{
    cout << str << n << endl;
}

int main()
{    
    int n1 = 1;
    string str1 = "hello";
    boost::thread t1(PrintThreadFunc, ref(n1), ref(str1));

    int n2 = 2;
    string str2 = "boost";
    function<void()> fun = bind(PrintThreadFunc, ref(n2), ref(str2));
    boost::thread t2(fun);

    t1.timed_join(boost::posix_time::seconds(1)); //最多等待1秒
    t2.join(); //一直等待

    return 0;
}
View Code

 2、線程的一些操作

  可以調用成員函數get_id()獲得線程ID,線程ID提供了比較操作符和流輸出操作,因此可以作為標准容器的元素。當一個線程的狀態是joinable的,那么可以調用成員函數get_id()獲得線程ID,線程ID提供了比較操作符和流輸出操作,因此可以作為標准容器的元素。如果調用了成員函數detach()將線程執行體分離,那么get_id()獲得的線程ID與靜態函數thread::id()的返回值相同。

  靜態函數this_thread::get_id()可以獲得當前線程的線程ID。

  靜態函數thread::this_thread::sleep()可以讓當前線程睡眠一段時間或到指定時間,

  靜態函數thread::hardware_concurrency()可以獲得當前CPU的內核數量。

  靜態函數this_thread::yield()指示當前線程放棄時間片,允許其他線程運行。

    boost::this_thread::sleep(boost::posix_time::seconds(2)); //睡眠2秒
    cout << boost::this_thread::get_id() << endl; //輸出當前線程ID
    cout << boost::thread::hardware_concurrency() << endl; //輸出CPU核心數
    boost::this_thread::yield(); //放棄當前CPU時間
View Code

 c++11中也有對應的操作,eg:

std::this_thread::sleep_for(std::chrono::seconds(5)); //睡眠5秒
std::this_thread::sleep_for(4ms); //睡眠4毫秒,需要引用命名空間:using namespace std::chrono

 3、線程中斷

  thread的成員函數interrupt()設置正在執行的線程被中斷,被中斷的線程會拋出一個thread_interrupted異常,它不是std::exception或boost::exception的子類。thread_interrupted異常應該在線程執行函數里捕獲並處理,如下所示,如果沒有捕獲處理這個異常,默認的動作是終止線程。

void ThreadFun()
try
{
    //函數體
}
catch (boost::thread_interrupted&)
{
    //異常處理
}
View Code

  線程其實不是任意時刻都能被中斷的,只有當線程執行到中斷點的時候才被中斷,thread中的中斷點有:thread::join()系列函數、thread::sleep()函數、condition_variable::wait()系列函數、this_thread::interruption_point()函數,其中this_thread::interruption_point()函數表示執行到本函數的時候就可以被中斷。

  缺省情況下線程都是允許中斷的,this_thread::interruption_enabled()函數可以檢測當前線程是否允許中斷,this_thread::interruption_requested()用來檢測當前線程是否被要求中斷。this_thread中的disable_interruption類是一個RAII類型的對象,它在構造的時候關閉線程的中斷,析構的時候恢復線程的中斷狀態。

 4、線程組

  線程組thread_group用於管理一組創建的線程,成員函數create_thread()可以創建thread對象並運行線程,也可以創建thread對象后使用成員函數add_thread()來加入線程組。成員函數create_thread()的聲明如下:

template<typename F> thread* create_thread(F threadfunc);

  成員函數remove_thread()可以刪除線程組里的thread對象,成員函數join_all()用來等待所有的thread對象,成員函數interrupt_all()用來中斷所有的thread對象。

  使用示例:

 boost::thread_group tg; int n1 = 0; tg.create_thread(bind(ThreadFun1, n1, "c++")); int n2 = 0; tg.create_thread(bind(ThreadFun2, n2, "python")); tg.join_all();
View Code

5、future

  如果想要獲得線程函數的返回值,可以使用future范式。

6、call_once()

  call_once()用來設置在多線程環境下指定的函數只被調用一次。

7、C++11中的線程

#include <thread>
void foo(int x)
{
    x = 0;
}

void bar(int& x)
{
    x = 0;
}

int main()
{    
    int n1 = 50, n2 = 100;
    std::thread first(foo, n1);
    std::thread second(bar, ref(n2));

    first.join(); //first.detach();
    second.join(); //second.detach();

    cout << n1 << endl; //n1為50
    cout << n2 << endl; //n2為0

    return 0;
}
View Code

  需要注意的幾點:

   ①、可執行的thread對象必須在他被銷毀之前被主線程join(調用thread對象的join())或者將其設置為 detached(調用thread對象的detach),否則會產生abort。

   ②、如果使用函數對象作為thread的參數的話,直接傳入臨時對象會出錯,可以定義一個對象傳入或者使用lambda表達式:

class CTask
{
public:
    void operator()()
    {
        int a = 0;
    }
};

//std::thread th1(CTask()); //直接傳入臨時對象會出錯
CTask task;
std::thread th1(task);
th1.join();
View Code

   ③、傳遞給線程函數的參數是先保存在於一個中轉站中,當函數執行的時候再傳給函數的形參,而這個時候傳遞的參數指向的值很有可能已經失效,所以,對於線程函數傳遞的參數應該與形參類型相同,而不是再進行轉換:

void task(int& a, string str)
    {

    }

    int iNum = 0;
    char* pStr = new char[100];
    strcpy(pStr, "test");
    //std::thread th(task, 5, std::ref(iNum), pStr); //不應該直接傳入pStr,防止task中還未對參數str初始化成功pStr已被釋放。
    std::thread th(task, std::ref(iNum), string(pStr)); //應該傳入對應類型
    delete[] pStr;
    th.join();
View Code

   ④、如果線程函數的參數是引用的話傳入時還需要使用ref包住傳入的參數,否則也是值傳遞。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM