天天看點

C++11多線程的原子操作

#include <iostream>
#include <string>
#include <list>
#include <ctime>
#include <mutex>
#include <atomic>
#include <thread>
#include <cassert>

// 1.所謂的原子操作,取的就是“原子是最小的、不可分割的最小個體”的意義,它表示在多個線程通路同一個全局資源的時候,
// 能夠確定所有其他的線程都不在同一時間内通路相同的資源。也就是他確定了在同一時刻隻有唯一的線程對這個資源進行通路。
// 這有點類似互斥對象對共享資源的通路的保護,但是原子操作更加接近底層,因而效率更高。

// 2.在新标準C++11,引入了原子操作的概念,并通過這個新的頭檔案提供了多種原子操作資料類型,例如,atomic_bool, atomic_int等等,
// 如果我們在多個線程中對這些類型的共享資源進行操作,編譯器将保證這些操作都是原子性的,也就是說,確定任意時刻隻有一個線程對這個
// 資源進行通路,編譯器将保證,多個線程通路這個共享資源的正确性。進而避免了鎖的使用,提高了效率。

// 3.所謂原子操作是指不會被線程排程機制打斷的操作;這種操作一旦開始,就一直運作到結束,中間不會有任何 context switch(切換到另一個線程)。

// 模拟點選操作,一百個使用者同時點選100000次。伺服器累計點選總次數,結果應為10000000。

typedef void(*ClickFunc)();

static long s_clicked_count = 0;
static std::atomic_long s_atomic_clicked_count;

static std::mutex s_mutex;

static void Click1() {
  for (long i = 0; i < 100000; ++i) {
    // 對全局資源進行無鎖通路,線程不安全導緻累加的結果不準确,大約幾十毫秒。
    s_clicked_count += 1;
  }
}

static void Click2() {
  for (long i = 0; i < 100000; ++i) {
    // 4.加鎖是一件十分耗時的操作,嚴重損耗性能。
    // 對全局資源進行加鎖通路,線程安全,結果正确,但是十分耗時大約兩分鐘。
    s_mutex.lock();
    s_clicked_count += 1;
    s_mutex.unlock();
  }
}

static void Click3() {
  // 5.原子操作的實作跟普通資料類型類似,但是它能夠在保證結果正确的前提下,提供比mutex等鎖機制更好的性能,
  // 如果我們要通路的共享資源可以用原子資料類型表示,那麼在多線程程式中使用這種新的等價資料類型。
  for (long i = 0; i < 1000000; ++i) {
    // 原子資料類型,線程安全,結果正确,大約兩秒。
    s_atomic_clicked_count += 1;
  }
}

static void Test(ClickFunc click_func) {
  s_clicked_count = 0;
  s_atomic_clicked_count = 0;

  clock_t start = clock();

  const int kThreadCount = 100;

  std::thread threads[kThreadCount];
  for (int i = 0; i < kThreadCount; ++i) {
    threads[i] = std::thread(click_func);
  }

  for (int i = 0; i < kThreadCount; ++i) {
    threads[i].join();
  }

  clock_t end = clock();

  std::cout << end - start << "ms. s_clicked_count: " << s_clicked_count;
  std::cout << " s_atomic_clicked_count: " << s_atomic_clicked_count << std::endl;
}

int main() {
  std::cout << "無鎖通路: ";
  Test(Click1);

  std::cout << "原子資料類型: ";
  Test(Click3);

  std::cout << "加鎖通路: ";
  Test(Click2);

  return 0;
}