目錄
從第一性原理出發:為什么需要線程?
?? 本質定義:
📌 使用基本語法:??
線程之間的“并發”與“并行”的區別
?線程安全與數據競爭(Race Condition)
如何讓線程“安全地”訪問數據??
完整示例:使用線程加速加法?
從第一性原理出發:為什么需要線程?
想象一下:
你有一個程序,它要做很多事,比如:
-
下載文件
-
處理圖片
-
打印日志
-
響應用戶輸入如果這些任務按順序來(單線程),那用戶體驗就會很差:比如下載還沒完,界面就卡住了。
第一性問題來了:?
能不能讓程序同時做多件事,而不是排隊一個個來??
?能!用線程!?
?? 本質定義:
?線程(Thread)是程序內部能并發執行的最小單位。
線程 = 程序里可以獨立并發執行的一段任務。?
一個程序至少有一個線程(主線程),你可以再創建多個線程來同時執行不同的代碼塊。
📌 使用基本語法:??
#include <thread>
?std::thread
類就是 C++ 提供的“創建和管理線程”的工具。
#include <iostream>
#include <thread>void work() {std::cout << "工作線程正在運行\n";
}int main() {std::thread t(work); // 創建一個線程執行 work()t.join(); // 等待這個線程執行完std::cout << "主線程結束\n";
}
解釋:
-
std::thread t(work);
:創建了一個“工人線程”,去執行函數work
-
t.join();
:告訴主線程“等工人做完再走” -
最后主線程輸出“主線程結束”
創建線程的三種方式:
方法 | 示例 |
---|---|
傳函數名(函數指針) | std::thread t(func); |
傳 lambda 表達式 | std::thread t([](){ ... }); |
傳函數對象(仿函數) | std::thread t(Functor()); |
?線程的控制操作(join
和 detach
)
std::thread t(func);
你創建了一個新線程,但它和主線程是“并行運行的”。
?接下來你必須做一件事(二選一):
操作 | 說明 |
---|---|
t.join() | 等待這個線程運行結束,和主線程“合并” |
t.detach() | 把線程“放飛”,讓它自己跑,主線程不再管它(后臺線程) |
C++ 標準要求:每個 std::thread
對象 在銷毀前必須被 join 或 detach!?
注意:主線程可能在子線程還沒結束時就退出了,這種線程叫做 后臺線程。?
線程之間的“并發”與“并行”的區別
概念 | 含義 |
---|---|
并發(concurrent) | 邏輯上“同時”運行,實際上輪流交替執行(單核CPU) |
并行(parallel) | 真正的同時執行(多核CPU,每個線程跑在不同核上) |
?線程安全與數據競爭(Race Condition)
多個線程訪問同一塊數據會出問題!?
int counter = 0;void add() {for (int i = 0; i < 100000; ++i)++counter;
}int main() {std::thread t1(add);std::thread t2(add);t1.join();t2.join();std::cout << counter << std::endl; // ? 輸出不是 200000?!
}
為什么?
-
因為兩個線程在同時修改同一個變量
-
出現了所謂的 數據競爭(Race Condition)
如何讓線程“安全地”訪問數據??
使用互斥鎖(std::mutex
)?
#include <mutex>std::mutex mtx;void add() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(mtx); // 自動加鎖/解鎖++counter;}
}
?使用 lock_guard
是現代 C++ 推薦的做法,RAII 自動釋放鎖。
完整示例:使用線程加速加法?
#include <iostream>
#include <thread>void printRange(int start, int end) {for (int i = start; i <= end; ++i)std::cout << i << " ";
}int main() {std::thread t1(printRange, 1, 50);std::thread t2(printRange, 51, 100);t1.join();t2.join();std::cout << "\nDone\n";
}
?輸出可能是:
1 2 3 51 52 53 ... 50 100
順序是“亂的”!因為是并發執行的結果。?