關於Windows的CONDITION_VARIABLE和C++11的condition_variable編寫代碼對比

Windows的CONDITION_VARIABLE使用CRITICAL_SECTION時,代碼如下

#include <Windows.h>
#include <iostream>
#include <thread>
#include <vector>

int Test(CONDITION_VARIABLE &cv, CRITICAL_SECTION &cs)
{
    EnterCriticalSection(&cs);
    SleepConditionVariableCS(&cv, &cs, INFINITE);
    std::cout << std::this_thread::get_id() << std::endl;
    LeaveCriticalSection(&cs);
    return 0;
}

int main()
{
    CONDITION_VARIABLE cv;
    InitializeConditionVariable(&cv);
    CRITICAL_SECTION cs;
    InitializeCriticalSection(&cs);

    std::vector<std::thread> vec_threads;

    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(cs));

    // 等待所有線程都已經處在等待條件變量的狀態
    std::this_thread::sleep_for(std::chrono::seconds(5));
    EnterCriticalSection(&cs);
    LeaveCriticalSection(&cs);
    
    // WakeConditionVariable(&cv);
    WakeAllConditionVariable(&cv);

    for (auto &t : vec_threads)
    {
        if (t.joinable())
        {
            t.join();
        }
    }

    DeleteCriticalSection(&cs);

    return 0;
}

Windows的CONDITION_VARIABLE使用SRWLOCK時,代碼如下

#include <Windows.h>
#include <iostream>
#include <thread>
#include <vector>

int Test(CONDITION_VARIABLE &cv, SRWLOCK &rwl)
{
   // 寫鎖,獨佔
   //AcquireSRWLockExclusive(&rwl);
   //SleepConditionVariableSRW(&cv, &rwl, INFINITE, 0);
   //std::cout << std::this_thread::get_id() << std::endl;
   //ReleaseSRWLockExclusive(&rwl);

   // 讀鎖,共享
   AcquireSRWLockShared(&rwl);
   SleepConditionVariableSRW(&cv, &rwl, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);
   std::cout << std::this_thread::get_id() << std::endl;
   ReleaseSRWLockShared(&rwl);

   return 0;
}

int main()
{
   CONDITION_VARIABLE cv;
   InitializeConditionVariable(&cv);
   SRWLOCK rwl;
   InitializeSRWLock(&rwl);

   std::vector<std::thread> vec_threads;

   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));
   vec_threads.emplace_back(Test, std::ref(cv), std::ref(rwl));

   // 等待所有線程都已經處在等待條件變量的狀態
   std::this_thread::sleep_for(std::chrono::seconds(5));
   AcquireSRWLockExclusive(&rwl);
   ReleaseSRWLockExclusive(&rwl);

   // WakeConditionVariable(&cv);
   WakeAllConditionVariable(&cv);

   for (auto &t : vec_threads)
   {
       if (t.joinable())
       {
           t.join();
       }
   }

   return 0;
}

C++11使用condition_variable的代碼如下

#include <condition_variable>
#include <mutex>
#include <iostream>
#include <thread>
#include <vector>

int Test(std::condition_variable &cv, std::mutex &mx)
{
    std::unique_lock<std::mutex> ul(mx);
    cv.wait(ul);
    std::cout << std::this_thread::get_id() << std::endl;
    return 0;
}

int main()
{
    std::condition_variable cv;
    std::mutex mx;

    std::vector<std::thread> vec_threads;

    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));
    vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));

    // 等待所有線程都已經處在等待條件變量的狀態
    std::this_thread::sleep_for(std::chrono::seconds(5));
    mx.lock();
    mx.unlock();

    // cv.notify_one();
    cv.notify_all();

    for (auto &t : vec_threads)
    {
        if (t.joinable())
        {
            t.join();
        }
    }
    return 0;
}

上述三份代碼在VS2017上編譯通過,可以看到,它們的區別是很小的。

需要注意的是上述三份代碼中啓動線程到喚醒條件變量處都有差不多意思的幾句代碼,如下

   // 等待所有線程都已經處在等待條件變量的狀態
   std::this_thread::sleep_for(std::chrono::seconds(5));
   EnterCriticalSection(&cs);
   LeaveCriticalSection(&cs);
   // 等待所有線程都已經處在等待條件變量的狀態
   std::this_thread::sleep_for(std::chrono::seconds(5));
   AcquireSRWLockExclusive(&rwl);
   ReleaseSRWLockExclusive(&rwl);
   // 等待所有線程都已經處在等待條件變量的狀態
   std::this_thread::sleep_for(std::chrono::seconds(5));
   mx.lock();
   mx.unlock();

這幾句代碼的作用都是一樣的,即等待所有線程都已經處在等待條件變量的狀態。由於當喚醒條件變量時,只會喚醒當前正在處在等待條件變量的線程,如果這時某個線程沒有處在等待條件變量,那麼當該線程運行到等待條件變量的代碼時,只會等待,並不會喚醒,即喚醒條件變量只能喚醒那一刻處在等待條件變量的線程,後面進入等待條件變量狀態的無法喚醒。

測試上述結論的代碼如下(使用std::condition_variable進行測試)

// 測試當線程不處在等待條件變量時執行notify_one的結果
#include <condition_variable>
#include <mutex>
#include <iostream>
#include <thread>
#include <vector>

int Test(std::condition_variable &cv, std::mutex &mx)
{
   std::unique_lock<std::mutex> ul(mx);
   while (true)
   {
       cv.wait(ul);
       std::cout << std::this_thread::get_id() << std::endl;
       std::this_thread::sleep_for(std::chrono::seconds(2));
   }
   
   return 0;
}

int main()
{
   std::condition_variable cv;
   std::mutex mx;

   std::vector<std::thread> vec_threads;

   vec_threads.emplace_back(Test, std::ref(cv), std::ref(mx));

   std::this_thread::sleep_for(std::chrono::seconds(1));
   cv.notify_one();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   cv.notify_one();
   cv.notify_one();
   
   for (auto &t : vec_threads)
   {
       if (t.joinable())
       {
           t.join();
       }
   }
   return 0;
}

上述代碼在VS2017上編譯通過,程序運行時可以看到只輸出了一次線程ID。

(PS:在MSDN上搜索CONDITION_VARIABLE出來的居然是C++11的std::condition_variable,吐血)。

以上就是本博客的全文,本人限於能力,上文中難免有錯誤的地方,若讀者發現上文的錯誤,請於評論區中指出,本人看到之後會立即修改的,謝謝。

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