C++實(shí)現(xiàn)多線程編程的方式匯總
在 C++ 中,實(shí)現(xiàn)多線程編程的方法有很多,從底層到高層大致可以分為以下幾類。
| 方式 | 標(biāo)準(zhǔn) | 是否推薦 | 適用場景 |
|---|---|---|---|
POSIX Threads (pthread) | POSIX | ???? | Linux/Unix 系統(tǒng)開發(fā) |
std::thread | C++11 | ????? | 現(xiàn)代 C++ 首選 |
std::async | C++11 | ??? | 簡單異步任務(wù) |
| OpenMP | 編譯器擴(kuò)展 | ????? | 科學(xué)計(jì)算、循環(huán)并行 |
| Intel TBB / oneTBB | 第三方庫 | ???? | 任務(wù)并行 |
| Boost.Thread | Boost | ??? | 老項(xiàng)目兼容 |
| Qt QThread | Qt | ???? | Qt GUI 程序 |
| MPI | 第三方庫 | ? | 多進(jìn)程,不屬于線程庫 |
注意最后一項(xiàng) MPI 不是線程庫,而是進(jìn)程并行庫。
1. std::thread(現(xiàn)代 C++ 首選)
C++11 開始標(biāo)準(zhǔn)提供。
#include <thread>
#include <iostream>
void work(int id)
{
std::cout << id << std::endl;
}
int main()
{
std::thread t1(work, 1);
std::thread t2(work, 2);
t1.join();
t2.join();
}
優(yōu)點(diǎn):
- 跨平臺
- STL 風(fēng)格
- 推薦新項(xiàng)目使用
配套還有:
std::mutex std::lock_guard std::unique_lock std::condition_variable std::atomic std::future
2. pthread(Linux 最經(jīng)典)
這是 Linux 系統(tǒng)接口。
#include <pthread.h>
void* func(void*)
{
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, func, nullptr);
pthread_join(tid, nullptr);
}
優(yōu)點(diǎn):
- Linux 下功能最完整
- 很多老項(xiàng)目都在用
缺點(diǎn):
- C 風(fēng)格 API
- 使用起來比較繁瑣
很多 std::thread 的實(shí)現(xiàn)底層實(shí)際上會(huì)調(diào)用 pthread(在 Linux 上)。
3. std::async
適合執(zhí)行一個(gè)異步任務(wù)。
auto result = std::async(std::launch::async,
[]()
{
return 100;
});
std::cout << result.get();
不需要自己創(chuàng)建線程。
4. OpenMP(科學(xué)計(jì)算最常見)
例如:
#pragma omp parallel for
for(int i=0;i<N;i++)
{
a[i]+=b[i];
}
編譯:
g++ -fopenmp
OpenMP 特點(diǎn):
- 幾乎不用管理線程
- 編譯器自動(dòng)完成
VASP、LAMMPS、OpenFOAM 都大量使用。
例如:
#pragma omp parallel
{
// 多線程
}
5. Intel TBB(oneTBB)
Intel 的任務(wù)調(diào)度庫。
例如:
tbb::parallel_for(
0,
N,
[&](int i)
{
...
});
比 OpenMP 更靈活。
很多工業(yè)軟件使用:
- OpenCV
- oneAPI
6. Boost.Thread
現(xiàn)代 C++ 出現(xiàn)以前最流行。
boost::thread t(func);
現(xiàn)在基本被:
std::thread
替代。
7. Qt 的 QThread
Qt GUI 開發(fā)必備。
例如:
class Worker : public QThread
{
void run() override
{
...
}
};
主要解決:
- GUI 不阻塞
- 信號槽
8. 線程池(Thread Pool)
嚴(yán)格來說不是一種線程 API,而是一種編程模式。
例如:
ThreadPool pool(8); pool.enqueue(task1); pool.enqueue(task2);
優(yōu)點(diǎn):
- 避免頻繁創(chuàng)建線程
- 性能最好
很多服務(wù)器都采用這種方式。
C++23 標(biāo)準(zhǔn)沒有正式線程池,很多項(xiàng)目使用:
- BS::thread_pool
- oneTBB
- folly
- Boost.Asio
9. MPI(容易和線程混淆)
MPI:
MPI_Init(...); MPI_Comm_rank(...); MPI_Send(...);
它創(chuàng)建的是:
Process0 Process1 Process2
不是:
Thread1 Thread2
所以:
MPI 是多進(jìn)程,不是多線程。
在 HPC 中通常是混合并行
像 VASP、GROMACS 等 HPC 軟件通常會(huì)組合使用多種技術(shù):
多個(gè)節(jié)點(diǎn)
│
▼
MPI(多進(jìn)程)
│
▼
OpenMP(每個(gè)進(jìn)程多個(gè)線程)
│
▼
CUDA/HIP(GPU 并行)
例如:
Node1
├── MPI Rank0
│ ├── Thread0
│ ├── Thread1
│ └── Thread2
└── MPI Rank1
├── Thread0
├── Thread1
└── GPU0
因此,一個(gè) VASP 作業(yè)中通常會(huì)同時(shí)用到 MPI + OpenMP + CUDA/HIP,分別負(fù)責(zé)不同層次的并行。
實(shí)際工作中的常見選擇
如果按行業(yè)來看,大致可以這樣歸納:
- 普通 C++ 應(yīng)用開發(fā):
std::thread是首選,Linux 老項(xiàng)目中pthread仍然很多。 - 科學(xué)計(jì)算 / HPC:OpenMP + MPI 是主流,GPU 場景再結(jié)合 CUDA/HIP。
- GUI 開發(fā)(Qt):通常使用
QThread。 - 高性能服務(wù)器:更多使用線程池、任務(wù)調(diào)度庫(如 oneTBB、Boost.Asio、folly 等),而不是頻繁創(chuàng)建和銷毀線程。
這張表有一定誤導(dǎo)性。MPI 和 OpenMP 并不是“見得少”,而是它們主要集中在科學(xué)計(jì)算和 HPC 領(lǐng)域;在通用 C++ 應(yīng)用、服務(wù)端和 GUI 開發(fā)中相對少見。
另外,現(xiàn)代 C++ 新項(xiàng)目通常優(yōu)先使用 std::thread,并不是普遍優(yōu)先使用 pthread。pthread 更多見于 Linux 系統(tǒng)軟件、底層運(yùn)行時(shí)和歷史項(xiàng)目。
先區(qū)分三種并行模型
| 技術(shù) | 并行單位 | 內(nèi)存模型 | 典型范圍 |
|---|---|---|---|
pthread / std::thread | 線程 | 共享內(nèi)存 | 單個(gè)進(jìn)程、單臺機(jī)器 |
| OpenMP | 線程 | 共享內(nèi)存 | 單進(jìn)程、單個(gè)計(jì)算節(jié)點(diǎn) |
| MPI | 進(jìn)程 | 分布式內(nèi)存、消息傳遞 | 單節(jié)點(diǎn)或多個(gè)計(jì)算節(jié)點(diǎn) |
MPI 不是線程庫。它主要解決:
節(jié)點(diǎn) A 的進(jìn)程如何和節(jié)點(diǎn) B 的進(jìn)程交換數(shù)據(jù)
OpenMP 和線程庫主要解決:
同一進(jìn)程內(nèi)多個(gè) CPU 核心如何共享數(shù)據(jù)并行計(jì)算
為什么通用 C++ 項(xiàng)目中 MPI 不常見
MPI 主要面向多進(jìn)程、分布式內(nèi)存計(jì)算。例如超算中:
計(jì)算節(jié)點(diǎn) 0:MPI rank 0~63 計(jì)算節(jié)點(diǎn) 1:MPI rank 64~127 計(jì)算節(jié)點(diǎn) 2:MPI rank 128~191
進(jìn)程之間通過消息通信:
MPI_Send(...); MPI_Recv(...); MPI_Allreduce(...);
它特別適合:
- VASP、LAMMPS、GROMACS 等科學(xué)計(jì)算;
- 流體、氣象、量子化學(xué)計(jì)算;
- 大規(guī)模矩陣和網(wǎng)格計(jì)算;
- 跨多個(gè)服務(wù)器或超算節(jié)點(diǎn)運(yùn)行。
普通桌面程序、Web 服務(wù)或 GUI 程序通常不需要同時(shí)使用幾十臺服務(wù)器做一次緊密耦合計(jì)算,因此很少使用 MPI。
另外,MPI 程序通常需要專門啟動(dòng):
mpirun -np 64 ./program
它的進(jìn)程部署、通信、負(fù)載均衡和故障處理也比普通線程復(fù)雜。
所以不是 MPI 不成熟,而是它解決的問題與普通多線程不同。在 HPC 中,MPI 非常常見,甚至可以說是基礎(chǔ)設(shè)施。
為什么通用 C++ 中 OpenMP 看起來也不多
OpenMP 最擅長的是規(guī)則循環(huán)并行:
#pragma omp parallel for
for (int i = 0; i < n; ++i) {
c[i] = a[i] + b[i];
}
這類代碼在科學(xué)計(jì)算中很多,因此 OpenMP 非常合適。
但通用 C++ 程序的并發(fā)任務(wù)通常不是規(guī)則循環(huán),例如:
- 網(wǎng)絡(luò)連接處理;
- 后臺任務(wù)隊(duì)列;
- 文件異步讀寫;
- GUI 后臺計(jì)算;
- 長生命周期工作線程;
- 生產(chǎn)者/消費(fèi)者;
- 線程池;
- 復(fù)雜任務(wù)依賴。
這些場景通常需要顯式控制:
創(chuàng)建線程 等待線程 條件變量 互斥鎖 任務(wù)隊(duì)列 線程生命周期 異常和退出
std::thread、std::jthread、線程池或異步框架更適合這些需求。
OpenMP 的優(yōu)勢是快速表達(dá)“把這一段計(jì)算并行化”,而不是構(gòu)建完整的并發(fā)軟件架構(gòu)。
為什么有些 C++ 項(xiàng)目使用 pthread
pthread 是 POSIX 線程接口,長期以來是 Linux/Unix 的底層線程標(biāo)準(zhǔn):
pthread_create(...); pthread_join(...); pthread_mutex_lock(...);
它常見的原因主要有以下幾個(gè)。
1. 歷史原因
C++11 以前,標(biāo)準(zhǔn) C++ 沒有正式的線程庫。Linux C/C++ 項(xiàng)目普遍使用 pthread。許多成熟項(xiàng)目延續(xù)了原有設(shè)計(jì)。
2. 同時(shí)支持 C 和 C++
pthread 是 C 接口,可以被 C 和 C++ 使用。操作系統(tǒng)、數(shù)據(jù)庫和底層庫經(jīng)常包含大量 C 代碼。
3. Linux 平臺的底層控制
pthread 提供一些平臺相關(guān)能力,例如:
pthread_setaffinity_np pthread_setschedparam pthread_setname_np
可用于:
- CPU 親和性;
- 實(shí)時(shí)調(diào)度策略;
- 線程棧大??;
- 線程名稱;
- NUMA 和性能調(diào)優(yōu)。
雖然 C++ 標(biāo)準(zhǔn)線程可以通過 native_handle() 獲取底層 pthread,但底層系統(tǒng)軟件有時(shí)會(huì)直接使用 pthread。
4. 很多高級線程庫最終建立在 pthread 上
在 Linux 中:
std::thread
OpenMP runtime
oneTBB
各種線程池
↓
通常最終使用 pthread/clone 創(chuàng)建系統(tǒng)線程
因此 pthread 是底層機(jī)制,而 OpenMP、oneTBB 和 std::thread 是更高級的編程接口。
新 C++ 項(xiàng)目該用什么
如果是普通現(xiàn)代 C++ 項(xiàng)目,通常優(yōu)先:
#include <thread>
std::thread t([] {
// 工作
});
t.join();
C++20 中更推薦考慮 std::jthread:
#include <thread>
std::jthread t([] {
// 工作
});
std::jthread 析構(gòu)時(shí)自動(dòng)等待線程,還支持協(xié)作式停止,比直接管理 std::thread 更安全。
只有在以下場景才更可能直接使用 pthread:
- 項(xiàng)目主體是 C;
- 必須兼容較老的 C++ 標(biāo)準(zhǔn);
- 強(qiáng)依賴 POSIX 接口;
- 需要精細(xì)控制線程調(diào)度、棧和親和性;
- 正在維護(hù)已有 pthread 項(xiàng)目。
OpenMP 和普通線程的核心差異
假設(shè)要并行處理數(shù)組。
使用 OpenMP:
#pragma omp parallel for
for (int i = 0; i < n; ++i) {
c[i] = a[i] + b[i];
}
程序員描述的是:
這個(gè)循環(huán)可以并行。
使用線程庫時(shí):
std::vector<std::thread> threads;
for (int t = 0; t < num_threads; ++t) {
threads.emplace_back([&, t] {
int begin = t * n / num_threads;
int end = (t + 1) * n / num_threads;
for (int i = begin; i < end; ++i) {
c[i] = a[i] + b[i];
}
});
}
for (auto& thread : threads) {
thread.join();
}
程序員需要自己處理:
- 創(chuàng)建多少線程;
- 每個(gè)線程處理哪一段;
- 如何捕獲變量;
- 如何等待線程結(jié)束。
因此對于規(guī)則數(shù)值循環(huán),OpenMP 更簡潔;對于復(fù)雜并發(fā)架構(gòu),顯式線程或任務(wù)系統(tǒng)更靈活。
MPI、OpenMP 經(jīng)常一起使用
在超算中,典型模式不是二選一,而是混合并行:
多個(gè)計(jì)算節(jié)點(diǎn)
↓ MPI
每個(gè)節(jié)點(diǎn)啟動(dòng)若干 MPI 進(jìn)程
↓ OpenMP
每個(gè) MPI 進(jìn)程再啟動(dòng)若干 CPU 線程
例如兩臺節(jié)點(diǎn),每臺 64 核:
每節(jié)點(diǎn) 4 個(gè) MPI 進(jìn)程 每個(gè) MPI 進(jìn)程 16 個(gè) OpenMP 線程 總計(jì):2 × 4 × 16 = 128 個(gè) CPU 核
常見啟動(dòng)方式類似:
export OMP_NUM_THREADS=16 mpirun -np 8 ./program
VASP 就經(jīng)常采用 MPI、OpenMP 和 GPU 加速的組合。
對原表的修正
更合理的評價(jià)是:
| 技術(shù) | 定位 | 推薦場景 |
|---|---|---|
std::thread / std::jthread | 標(biāo)準(zhǔn) C++ 線程 | 現(xiàn)代通用 C++ |
| pthread | POSIX 底層線程 | C、Linux 系統(tǒng)開發(fā)、底層控制 |
| OpenMP | 標(biāo)準(zhǔn)化共享內(nèi)存并行 API | 科學(xué)計(jì)算、規(guī)則循環(huán) |
| oneTBB | 任務(wù)并行運(yùn)行時(shí) | 復(fù)雜任務(wù)圖、線程池 |
| MPI | 分布式進(jìn)程通信標(biāo)準(zhǔn) | 集群、超算、多節(jié)點(diǎn)計(jì)算 |
另外,OpenMP 不是簡單的“編譯器私有擴(kuò)展”,它有獨(dú)立的公開規(guī)范,只是通過編譯器指令和運(yùn)行時(shí)庫實(shí)現(xiàn)。
一句話總結(jié):
普通 C++ 并發(fā)優(yōu)先考慮標(biāo)準(zhǔn)線程或成熟任務(wù)框架;規(guī)則科學(xué)計(jì)算優(yōu)先考慮 OpenMP;跨節(jié)點(diǎn)并行使用 MPI;pthread 更接近 Linux 底層實(shí)現(xiàn)和控制接口。
以上就是C++實(shí)現(xiàn)多線程編程的方式匯總的詳細(xì)內(nèi)容,更多關(guān)于C++多線程編程方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能
所謂高斯噪聲是指它的概率密度函數(shù)服從高斯分布(即正態(tài)分布)的一類噪聲,這篇文章主要給大家介紹了關(guān)于C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能的相關(guān)資料,需要的朋友可以參考下2021-07-07
solaris操作系統(tǒng)做c應(yīng)用程序開發(fā)步驟
solaris操作系統(tǒng)做c應(yīng)用程序開發(fā)步驟,大家參考使用吧2013-12-12
詳解Dev C++使用教程(使用Dev C++編寫C語言程序)
這篇文章主要介紹了詳解Dev C++使用教程(使用Dev C++編寫C語言程序),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
C++中的移動(dòng)構(gòu)造函數(shù)及move語句示例詳解
這篇文章主要給大家介紹了關(guān)于C++中移動(dòng)構(gòu)造函數(shù)及move語句的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10

