【C++併發與多線程】 12_recursive_mutex、timed_mutex、recursive_timed_mutex

window 臨界區

  • window 臨界區資源對象與C++的 std::mutex 對象相似,能夠保護多個線程對臨界區資源的訪問。
#include <iostream>
#include <thread>
#include <Windows.h>

static CRITICAL_SECTION g_winsec;

void print_block (int n, char c)
{
  EnterCriticalSection(&g_winsec);      // 2. 進入臨界區

  for (int i=0; i<n; ++i) {
      std::cout << c;
  }

  std::cout << '\n';

  LeaveCriticalSection(&g_winsec);      // 3. 離開臨界區
}

int main ()
{
  InitializeCriticalSection(&g_winsec); // 1. 初始化臨界資源對象

  std::thread th1 (print_block,50,'*');
  std::thread th2 (print_block,50,'$');

  th1.join();
  th2.join();

  return 0;
}

輸出:ios

**************************************************
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

屢次進入臨界區實驗

  • window 臨界資源對象能夠在同一線程中屢次重複進入,對應次數的離開,程序仍正常執行。
  • std::mutex 對象只能在同一線程進行一次加鎖並對應一次解鎖,不然程序拋出異常。

測試1:window 臨界區ide

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

static CRITICAL_SECTION g_winsec;

void print_block (int n, char c)
{
  EnterCriticalSection(&g_winsec);      // 2. 進入臨界區
  EnterCriticalSection(&g_winsec);      // 屢次進入 。。。
  EnterCriticalSection(&g_winsec);      // 屢次進入 。。。

  for (int i=0; i<n; ++i) {
      std::cout << c;
  }

  std::cout << '\n';

  LeaveCriticalSection(&g_winsec);      // 3. 離開臨界區
  LeaveCriticalSection(&g_winsec);      // 屢次離開 。。。
  LeaveCriticalSection(&g_winsec);      // 屢次離開 。。。
}

int main ()
{
  InitializeCriticalSection(&g_winsec); // 1. 初始化臨界資源對象

  std::thread th1 (print_block,50,'*');
  std::thread th2 (print_block,50,'$');

  th1.join();
  th2.join();

  return 0;
}

輸出:[結果正確]函數

**************************************************
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

測試2:std::mutex測試

// mutex example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex

std::mutex mtx;           // mutex for critical section

void print_block (int n, char c) {
  // critical section (exclusive access to std::cout signaled by locking mtx):
  mtx.lock();
  mtx.lock();
  mtx.lock();
  for (int i=0; i<n; ++i) { std::cout << c; }
  std::cout << '\n';
  mtx.unlock();
  mtx.unlock();
  mtx.unlock();
}

int main ()
{
  std::thread th1 (print_block,50,'*');
  std::thread th2 (print_block,50,'$');

  th1.join();
  th2.join();

  return 0;
}

輸出:優化

程序異常退出

自動析構技術

  • RAII(Resource Acquisition Is Initialization),也稱爲「資源獲取就是初始化」,是C++語言的一種管理資源、避免泄漏的慣用法。
  • C++標準保證任何狀況下,已構造的對象最終會銷燬,即它的析構函數最終會被調用。簡單的說,RAII 的作法是使用一個對象,在其構造時獲取資源,在對象生命期控制對資源的訪問使之始終保持有效,最後在對象析構的時候釋放資源。
#include <iostream>
#include <thread>
#include <Windows.h>

static CRITICAL_SECTION g_winsec;

class CWinLock {
public:
    CWinLock(CRITICAL_SECTION *winsec) : m_winsec(winsec)
    {
         EnterCriticalSection(m_winsec); // 進入臨界區
    }

    ~CWinLock()
    {
        LeaveCriticalSection(m_winsec);  // 離開臨界區
    }

private:
    CRITICAL_SECTION *m_winsec = nullptr;
};

void print_block (int n, char c)
{
  CWinLock win_lock(&g_winsec);

  for (int i=0; i<n; ++i) {
      std::cout << c;
  }

  std::cout << '\n';
}

int main ()
{
  InitializeCriticalSection(&g_winsec); // 1. 初始化臨界資源對象

  std::thread th1 (print_block,50,'*');
  std::thread th2 (print_block,50,'$');

  th1.join();
  th2.join();

  return 0;
}

輸出:ui

**************************************************
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

std::recursive_mutex

  • 就像互斥鎖(mutex)同樣,遞歸互斥鎖(recursive_mutex)是可鎖定的對象,但它容許同一線程得到對互斥鎖對象的多級全部權(屢次lock)。
  • 這容許從已經鎖定它的線程鎖定(或嘗試鎖定)互斥對象,從而得到對互斥對象的新全部權級別:互斥對象實際上將保持對該線程的鎖定,直到調用其成員 unlock 的次數與此全部權級別的次數相同。
try_lock 若是沒有被其它線程鎖定,則鎖定互斥鎖
unlock 解鎖互斥鎖

測試:僅演示說明,使用 recursive_mutex 時需考慮是否存在優化空間!this

#include <iostream>
#include <thread>
#include <mutex> 

std::recursive_mutex mtx;           

void print_block (int n, char c) {
  mtx.lock();
  mtx.lock();
  mtx.lock();
  
  for (int i=0; i<n; ++i) { std::cout << c; }
  std::cout << '\n';
  
  mtx.unlock();
  mtx.unlock();
  mtx.unlock();
}

int main ()
{
  std::thread th1 (print_block,50,'*');
  std::thread th2 (print_block,50,'$');

  th1.join();
  th2.join();

  return 0;
}

輸出:spa

**************************************************
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

std::timed_mutex、std::recursive_timed_mutex

std::timed_mutex

  • 定時互斥鎖是一個可時間鎖定的對象,旨在通知什麼時候關鍵代碼須要獨佔訪問,就像常規互斥鎖同樣,但還支持定時嘗試鎖定請求。
lock 調用線程將鎖定timed_mutex,並在必要時進行阻塞(其行爲與 mutex 徹底相同)
try_lock 嘗試鎖定 timed_mutex,而不進行阻塞(其行爲與互斥鎖徹底相同)
try_lock_for 嘗試鎖定 timed_mutex, 最多阻塞 rel_time 時間
try_lock_until 嘗試鎖定 timed_mutex,最多阻塞到 abs_time 時間點
unlock 解鎖 timed_mutex,釋放對其的全部權(其行爲與 mutex 相同)

測試1: try_lock_for線程

// timed_mutex::try_lock_for example
#include <iostream>       // std::cout
#include <chrono>         // std::chrono::milliseconds
#include <thread>         // std::thread
#include <mutex>          // std::timed_mutex

std::timed_mutex mtx;

void fireworks () {
  // waiting to get a lock: each thread prints "-" every 200ms:
  while (!mtx.try_lock_for(std::chrono::milliseconds(200))) {
    std::cout << "-";
  }
  // got a lock! - wait for 1s, then this thread prints "*"
  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  std::cout << "*\n";
  mtx.unlock();
}

int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(fireworks);

  for (auto& th : threads) th.join();

  return 0;
}

輸出:code

------------------------------------*
----------------------------------------*
-----------------------------------*
------------------------*
-------------------------*
--------------------*
---------------*
--------*
-----*
*

測試2:try_lock_until

// timed_mutex::try_lock_until example
#include <iostream>       // std::cout
#include <chrono>         // std::chrono::system_clock
#include <thread>         // std::thread
#include <mutex>          // std::timed_mutex
#include <ctime>          // std::time_t, std::tm, std::localtime, std::mktime

std::timed_mutex cinderella;

// gets time_point for next midnight:
std::chrono::time_point<std::chrono::system_clock> midnight() {
  using std::chrono::system_clock;
  std::time_t tt = system_clock::to_time_t (system_clock::now());
  struct std::tm * ptm = std::localtime(&tt);
  ++ptm->tm_mday; ptm->tm_hour=0; ptm->tm_min=0; ptm->tm_sec=0;
  return system_clock::from_time_t (mktime(ptm));
}

void carriage() {
  if (cinderella.try_lock_until(midnight())) {
    std::cout << "ride back home on carriage\n";
    cinderella.unlock();
  }
  else
    std::cout << "carriage reverts to pumpkin\n";
}

void ball() {
  cinderella.lock();
  std::cout << "at the ball...\n";
  cinderella.unlock();
}

int main ()
{
  std::thread th1 (ball);
  std::thread th2 (carriage);

  th1.join();
  th2.join();

  return 0;
}

輸出:

at the ball...
ride back home on carriage

std::recursive_timed_mutex

  • 遞歸定時互斥鎖將 recursive_timed 和 timed_mutex 的功能結合到一個類中:它既支持經過單個線程獲取多個鎖定級別又支持定時的 try_lock 請求。
  • 成員函數與 timed_mutex 相同。
相關文章
相關標籤/搜索