C++ Concurrency

std::thread

C++11 引入了標準的 thread 函式庫,用法如下:

#include <iostream>
#include <thread>

int main() {
    std::thread t([](){
        std::cout << "hello world." << std::endl;
    });
    t.join();
    return 0;
}

Locks

  • std::mutex : lock 的基本元件,提供 lock 與 unlock 功能建立 critical section
    • 不建議直接使用,因為需要記得手動 lock 與 unlock
  • std::lock_guard : std::mutex 的 wrapper,創建時自動 lock mutex,解構時自動 unlock
  • std::unique_lock : std::mutex 的 wrapper,創建時自動 lock,中途可以選擇 unlock 並再度 lock,解構時會自動 unlock
    • 建議優先選擇用這個

    • 範例:

      #include <iostream>
      #include <mutex>
      #include <thread>
      
      int v = 1;
      
      void critical_section(int change_v) {
          static std::mutex mtx;
          std::unique_lock<std::mutex> lock(mtx);
          // do contention operations
          v = change_v;
          std::cout << v << std::endl;
          // release the lock
          lock.unlock();
      
          // during this period, others are allowed to acquire v
      
          // start another group of contention operations
          // lock again
          lock.lock();
          v += 1;
          std::cout << v << std::endl;
          // Unlock when it disappears
      }
      
      int main() {
          std::thread t1(critical_section, 2), t2(critical_section, 3);
          t1.join();
          t2.join();
          return 0;
      }
      

std::future

一個用來建立 task,等待未來回收結果的 utility 元件。

詳情:https://changkun.de/modern-cpp/en-us/07-thread/#7-3-Future

std::condition_variable

提供 wait()notify_all()notify_one() 的功能,讓 thread 可以進入等待狀態,其他 thread 也可以叫醒等待的 thread

範例:https://changkun.de/modern-cpp/en-us/07-thread/#7-4-Condition-Variable

std::atomic

如果想要保證一些常見操作(例如 ++)的原子性,除了使用 std::mutex 建立 critical section 之外,也可以使用 std::atomic 。 這樣可以大幅減少要撰寫的程式碼。 下面展示使用 std::atomic 做出 atomic 的 ++ 操作的範例:

#include <atomic>
#include <thread>
#include <iostream>

std::atomic<int> count = {0};

int main() {
    std::thread t1([](){
        count.fetch_add(1);
    });
    std::thread t2([](){
        count++;        // identical to fetch_add
        count += 1;     // identical to fetch_add
    });
    t1.join();
    t2.join();
    std::cout << count << std::endl;
    return 0;
}

因為 std::atomic 實際上是一個 template,可以容納任何類型的資料,因此 C++ 有提供額外的 API 檢查該類型資料是否有保障原子性:

#include <atomic>
#include <iostream>

struct A {
    float x;
    int y;
    long long z;
};

int main() {
    std::atomic<A> a;
    std::cout << std::boolalpha << a.is_lock_free() << std::endl;
    return 0;
}