it编程 > 编程语言 > C/C++

C++实现多线程编程的方式汇总

32人参与 2026-07-26 C/C++

在 c++ 中,实现多线程编程的方法有很多,从底层到高层大致可以分为以下几类。

方式标准是否推荐适用场景
posix threads (pthread)posix⭐⭐⭐⭐linux/unix 系统开发
std::threadc++11⭐⭐⭐⭐⭐现代 c++ 首选
std::asyncc++11⭐⭐⭐简单异步任务
openmp编译器扩展⭐⭐⭐⭐⭐科学计算、循环并行
intel tbb / onetbb第三方库⭐⭐⭐⭐任务并行
boost.threadboost⭐⭐⭐老项目兼容
qt qthreadqt⭐⭐⭐⭐qt gui 程序
mpi第三方库多进程,不属于线程库

注意最后一项 mpi 不是线程库,而是进程并行库

1. std::thread(现代 c++ 首选)

c++11 开始标准提供。

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

优点:

配套还有:

std::mutex
std::lock_guard
std::unique_lock
std::condition_variable
std::atomic
std::future

2. pthread(linux 最经典)

这是 linux 系统接口。

#include <pthread.h>

void* func(void*)
{
    return nullptr;
}

int main()
{
    pthread_t tid;

    pthread_create(&tid, nullptr, func, nullptr);

    pthread_join(tid, nullptr);
}

优点:

缺点:

很多 std::thread 的实现底层实际上会调用 pthread(在 linux 上)。

3. std::async

适合执行一个异步任务。

auto result = std::async(std::launch::async,
                         []()
                         {
                             return 100;
                         });

std::cout << result.get();

不需要自己创建线程。

4. openmp(科学计算最常见)

例如:

#pragma omp parallel for
for(int i=0;i<n;i++)
{
    a[i]+=b[i];
}

编译:

g++ -fopenmp

openmp 特点:

vasp、lammps、openfoam 都大量使用。

例如:

#pragma omp parallel
{
    // 多线程
}

5. intel tbb(onetbb)

intel 的任务调度库。

例如:

tbb::parallel_for(
    0,
    n,
    [&](int i)
    {
        ...
    });

比 openmp 更灵活。

很多工业软件使用:

6. boost.thread

现代 c++ 出现以前最流行。

boost::thread t(func);

现在基本被:

std::thread

替代。

7. qt 的 qthread

qt gui 开发必备。

例如:

class worker : public qthread
{
    void run() override
    {
        ...
    }
};

主要解决:

8. 线程池(thread pool)

严格来说不是一种线程 api,而是一种编程模式。

例如:

threadpool pool(8);

pool.enqueue(task1);
pool.enqueue(task2);

优点:

很多服务器都采用这种方式。

c++23 标准没有正式线程池,很多项目使用:

9. mpi(容易和线程混淆)

mpi:

mpi_init(...);

mpi_comm_rank(...);

mpi_send(...);

它创建的是:

process0
process1
process2

不是:

thread1
thread2

所以:

mpi 是多进程,不是多线程。

在 hpc 中通常是混合并行

像 vasp、gromacs 等 hpc 软件通常会组合使用多种技术:

多个节点
    │
    ▼
 mpi(多进程)
    │
    ▼
openmp(每个进程多个线程)
    │
    ▼
cuda/hip(gpu 并行)

例如:

node1
 ├── mpi rank0
 │      ├── thread0
 │      ├── thread1
 │      └── thread2
 └── mpi rank1
        ├── thread0
        ├── thread1
        └── gpu0

因此,一个 vasp 作业中通常会同时用到 mpi + openmp + cuda/hip,分别负责不同层次的并行。

实际工作中的常见选择

如果按行业来看,大致可以这样归纳:

这张表有一定误导性。mpi 和 openmp 并不是“见得少”,而是它们主要集中在科学计算和 hpc 领域;在通用 c++ 应用、服务端和 gui 开发中相对少见。

另外,现代 c++ 新项目通常优先使用 std::thread,并不是普遍优先使用 pthreadpthread 更多见于 linux 系统软件、底层运行时和历史项目。

先区分三种并行模型

技术并行单位内存模型典型范围
pthread / std::thread线程共享内存单个进程、单台机器
openmp线程共享内存单进程、单个计算节点
mpi进程分布式内存、消息传递单节点或多个计算节点

mpi 不是线程库。它主要解决:

节点 a 的进程如何和节点 b 的进程交换数据

openmp 和线程库主要解决:

同一进程内多个 cpu 核心如何共享数据并行计算

为什么通用 c++ 项目中 mpi 不常见

mpi 主要面向多进程、分布式内存计算。例如超算中:

计算节点 0:mpi rank 0~63
计算节点 1:mpi rank 64~127
计算节点 2:mpi rank 128~191

进程之间通过消息通信:

mpi_send(...);
mpi_recv(...);
mpi_allreduce(...);

它特别适合:

普通桌面程序、web 服务或 gui 程序通常不需要同时使用几十台服务器做一次紧密耦合计算,因此很少使用 mpi。

另外,mpi 程序通常需要专门启动:

mpirun -np 64 ./program

它的进程部署、通信、负载均衡和故障处理也比普通线程复杂。

所以不是 mpi 不成熟,而是它解决的问题与普通多线程不同。在 hpc 中,mpi 非常常见,甚至可以说是基础设施。

为什么通用 c++ 中 openmp 看起来也不多

openmp 最擅长的是规则循环并行:

#pragma omp parallel for
for (int i = 0; i < n; ++i) {
    c[i] = a[i] + b[i];
}

这类代码在科学计算中很多,因此 openmp 非常合适。

但通用 c++ 程序的并发任务通常不是规则循环,例如:

这些场景通常需要显式控制:

创建线程
等待线程
条件变量
互斥锁
任务队列
线程生命周期
异常和退出

std::threadstd::jthread、线程池或异步框架更适合这些需求。

openmp 的优势是快速表达“把这一段计算并行化”,而不是构建完整的并发软件架构。

为什么有些 c++ 项目使用 pthread

pthread 是 posix 线程接口,长期以来是 linux/unix 的底层线程标准:

pthread_create(...);
pthread_join(...);
pthread_mutex_lock(...);

它常见的原因主要有以下几个。

1. 历史原因

c++11 以前,标准 c++ 没有正式的线程库。linux c/c++ 项目普遍使用 pthread。许多成熟项目延续了原有设计。

2. 同时支持 c 和 c++

pthread 是 c 接口,可以被 c 和 c++ 使用。操作系统、数据库和底层库经常包含大量 c 代码。

3. linux 平台的底层控制

pthread 提供一些平台相关能力,例如:

pthread_setaffinity_np
pthread_setschedparam
pthread_setname_np

可用于:

虽然 c++ 标准线程可以通过 native_handle() 获取底层 pthread,但底层系统软件有时会直接使用 pthread。

4. 很多高级线程库最终建立在 pthread 上

在 linux 中:

std::thread
openmp runtime
onetbb
各种线程池
        ↓
通常最终使用 pthread/clone 创建系统线程

因此 pthread 是底层机制,而 openmp、onetbb 和 std::thread 是更高级的编程接口。

新 c++ 项目该用什么

如果是普通现代 c++ 项目,通常优先:

#include <thread>

std::thread t([] {
    // 工作
});
t.join();

c++20 中更推荐考虑 std::jthread

#include <thread>

std::jthread t([] {
    // 工作
});

std::jthread 析构时自动等待线程,还支持协作式停止,比直接管理 std::thread 更安全。

只有在以下场景才更可能直接使用 pthread:

openmp 和普通线程的核心差异

假设要并行处理数组。

使用 openmp:

#pragma omp parallel for
for (int i = 0; i < n; ++i) {
    c[i] = a[i] + b[i];
}

程序员描述的是:

这个循环可以并行。

使用线程库时:

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();
}

程序员需要自己处理:

因此对于规则数值循环,openmp 更简洁;对于复杂并发架构,显式线程或任务系统更灵活。

mpi、openmp 经常一起使用

在超算中,典型模式不是二选一,而是混合并行:

多个计算节点
    ↓ mpi
每个节点启动若干 mpi 进程
    ↓ openmp
每个 mpi 进程再启动若干 cpu 线程

例如两台节点,每台 64 核:

每节点 4 个 mpi 进程
每个 mpi 进程 16 个 openmp 线程
总计:2 × 4 × 16 = 128 个 cpu 核

常见启动方式类似:

export omp_num_threads=16
mpirun -np 8 ./program

vasp 就经常采用 mpi、openmp 和 gpu 加速的组合。

对原表的修正

更合理的评价是:

技术定位推荐场景
std::thread / std::jthread标准 c++ 线程现代通用 c++
pthreadposix 底层线程c、linux 系统开发、底层控制
openmp标准化共享内存并行 api科学计算、规则循环
onetbb任务并行运行时复杂任务图、线程池
mpi分布式进程通信标准集群、超算、多节点计算

另外,openmp 不是简单的“编译器私有扩展”,它有独立的公开规范,只是通过编译器指令和运行时库实现。

一句话总结:

普通 c++ 并发优先考虑标准线程或成熟任务框架;规则科学计算优先考虑 openmp;跨节点并行使用 mpi;pthread 更接近 linux 底层实现和控制接口。

以上就是c++实现多线程编程的方式汇总的详细内容,更多关于c++多线程编程方式的资料请关注代码网其它相关文章!

(0)

您想发表意见!!点此发布评论

推荐阅读

C++ 多态底层原理解析:静态绑定、动态绑定、虚函数表与工程实践

07-28

C语言分支与循环语句的完全指南

07-28

C++中使用yaml-cpp库处理YAML配置文件的完整指南

07-24

C++奇异递归模板(CRTP)的使用小结

07-24

C++中虚函数表的初始化时机

07-24

C++11从可变参数模板到bind绑定器的详细过程

07-29

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论