STL線程庫簡介


STL線程庫的前身是boost::thread,在C++ 11標准化后便正式歸納入了stl庫,通過它我們可以很容易實現跨平台的線程管理。

線程管理

在std::thread庫中,一個線程用的是一個thread對象表示,當創建一個thread對象時即創建一個線程,一個簡單的示例如下:

    #include <iostream>
    #include <thread>
    using namespace std;

    void thread_entry(const char* arg)
    {
        cout << "thread "<<
this_thread::get_id() << " created: " << arg << endl;
    }

    int main()
    {
        thread
thrd(thread_entry, "hello world");
        
thrd.join();
        return 0;
    }

這里我通過thread對象創建了一個線程,並調用join函數等待線程結束。thead對象的構造函數中是可以傳入參數的,非常方便。

std::this_thread名字空間

在前面的例子中,還用到了一個this_thread::get_id()函數用以獲取當前線程的tid,std::this_thread名字空間提供了如下函數以管理當前線程:

  • yield
  • get_id
  • sleep_for
  • sleep_until

基本上從名字里就可以猜出它們的功能了。以其中的sleep_for函數為例,它提供了一個跨平台的sleep功能,再也不用自己封裝了:

    std::chrono::milliseconds dura(2000);
    std::this_thread::sleep_for(dura);

PS:在gcc中使用這個函數時,需要再編譯的時候加-D_GLIBCXX_USE_NANOSLEEP選項,否則報語法錯誤,具體原因可以參看這篇文章

Linux平台的運行錯誤

上述代碼在Windows平台運行良好,但在Linux上運行的時候發現如下錯誤:

    tianfang > run
    terminate called after throwing an instance of 'std::system_error'
        what(): Operation not permitted
    Aborted
    tianfang >

然后在StackOverFlow上找到了答案:在鏈接的時候要加 –pthread 選項。這個應該算個bug了。

刪除Thread對象

我最初以為當thread對象刪除時會自動殺掉線程,運行時卻發現:thread對象刪除時,如果沒有調用join或attach,就會報異常。為了演示這一過程,首先我們把main函數改成如下所示:

    int main()
    {
        thread thrd(thread_entry, "hello world");
        //thrd.join();
        return 0;
    }

運行該代碼時,就會發現如下錯誤:

    tianfang > run
    terminate called without an active exception
    Aborted
    tianfang >

也就是說,thread對象並不會自動管理線程結束,需要手動控制。常見的控制手段有兩個,join和detach。join已經介紹過,用於等待線程結束,而detach的功能是托管線程,線程仍然繼續運行至結束,但不再受到thread對象控制。

對線程編程模型比較熟悉的人可能會發現:它並沒有提供一個強制終止線程的選項。在boost的this_thread名字空間下其實是提供了終止線程的函數的,但是並沒有納入stl中,可見標准委員會也是不建議強制終止這種不健壯的做法。

互斥和同步

互斥體

stl中對mutex划分得很細,按是否遞歸、是否超時分為四個對象:

  • mutex
  • timed_mutex
  • recursive_mutex
  • recursive_timed_mutex

這四個對象的操作函數大體上都是如下幾個:

  • lock    獲取鎖
  • trylock    嘗試獲取鎖
  • unlock    釋放鎖
  • try_lock_for    在一定時間范圍內嘗試獲取鎖(有超時功能的mutex才能用)
  • try_lock_until    嘗試獲取鎖到某個時間點位置(有超時功能的mutex才能用)

除了直接使用這幾個類外,也可以使用lock_guar、unique_lock之類的的封裝類,它的功能上類似C#的lock關鍵字(lock范圍並不等價),在指定范圍內自動獲取鎖,出了該范圍外自動解鎖,可有效防止加解鎖不對稱。

另外,stl也提供了try_lock和lock的泛型方法實現批量獲取鎖定,這里就不多介紹了。

條件變量

stl中對條件變量封裝的是condition_variable類,它的基本使用方式和系統api差不多,如下是一個生產者/消費者的例子:

    #include <condition_variable>
    #include <mutex>
    #include <thread>
    #include <iostream>
    #include <queue>
    #include <chrono>
    using namespace std;

    int main()
    {
        queue<int> buffer;
        mutex m;
        condition_variable cond_var;
        int num = 0;

        thread producer([&]()
        {
            while (true)
            {
                this_thread::sleep_for(chrono::seconds(1));

                unique_lock<std::mutex> lock(m);

                num++;
                std::cout << "producing " << num << '\n';
                buffer.push(num);

                cond_var.notify_one();
            }
        });

        thread consumer([&]()
        {
            while (true)
            {
                unique_lock<std::mutex> lock(m);

                if(buffer.empty())
                    cond_var.wait(lock);

                std::cout << "consuming " << buffer.front() << '\n';
                buffer.pop();
            }
        });

        producer.join();
        consumer.join();
    }

條件變量還有其它幾個封裝,這里就不多介紹了。

其它

其它幾個同步互斥的類,如柵欄、讀寫鎖等並沒有納入STL,如果要使用它們,可以直接使用boost庫。


免責聲明!

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



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