多線程協同之 條件變量: condition_variable

理解

條件變量(condition_variable) 在cppreference.com 的解釋簡單概括爲: 用於阻塞一個或者多個線程,直到另外一個線程喚醒他們。在多線程變成中,可能爲多個線程協同完成,在需要多線程同步的場景就可以使用條件變量,舉個例子:

一個進程要實現功能:渲染線程負責從camera獲取圖像,並且渲染;主線程負責控制渲染線程的顯示和隱藏。簡單的實現可能是:

  •  主線程在需要的時候,創建渲染線程,渲染線程初始化,這個過程可能包括創建surface, EGL的上下文和weston的事件監聽等。
  • 主線程負責給渲染線程發指令,顯示和隱藏。子線程接受到信號後顯示和隱藏

因此線程同步的需求就發生了:主線程需要等待子線程完成初始化,才能響應主線程的指令,因此主線程需要等待子線程達到某種條件纔行。

實現

具體實現起來需要具備三個因素:

例子

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
 
std::mutex m;
std::condition_variable cv;
std::string data;
bool mainready = false;
bool renderready = false;
void render_thread()
{
    // Wait until main() sends data
    std::cout << "render_thread begin\n";
    std::unique_lock<std::mutex> lk(m);
    std::cout << "render_thread wait main thread\n";
    // cv.wait(lk);
    // cv.wait(lk, []{return mainready; });
    cv.wait_for(lk,std::chrono::milliseconds(100), []{return mainready; });
    std::cout << "render_thread begin init\n";
    /*
        init something
    */
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "init something done\n";
    renderready = true;
    cv.notify_one();
}
 
int main()
{
    std::thread worker(render_thread);
    // send data to the worker thread
    {
        std::cout << "main thread begin to init \n";
        std::lock_guard<std::mutex> lk(m);
        /*  do something in main thread */
        mainready = true;
        std::cout << "main thread is ready\n";
    }
    cv.notify_one();
    // wait for render thread ready
    {
        std::unique_lock<std::mutex> lk(m);
        std::cout << "main thread begin to wait render thread ready " << '\n';
        cv.wait(lk, []{return renderready;});
    }
    std::cout << "now can send cmd to render thread " << '\n';
    worker.join();
}

輸出

render_thread begin
render_thread wait main thread
main thread begin to init
main thread is ready
main thread begin to wait render thread ready
render_thread begin init
init something done
now can send cmd to render thread

注意

主線程跟渲染線程實際上在競賽,那麼主線程和渲染線程的等待情況就可能出現多種。看上面實例中的這幾行代碼。

    // cv.wait(lk);
    // cv.wait(lk, []{return mainready; });
    cv.wait_for(lk,std::chrono::milliseconds(100), []{return mainready; });

針對如果主線程的notify的時機早於渲染線程wait的時機的情況:

第一種寫法,那麼單純的wait就可能出現死等的情況。

第二種寫法,測試下來沒有問題,依賴Spurious wakeup,但是文章指出會存在不可預測的邊緣效應

推薦第三種寫法。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章