#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;
}