C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++线程创建方法

C++创建线程的几种方法详解

作者:Lzg_na

从 C++11 开始,标准库正式引入了线程支持,让开发者无需依赖平台特定的 API即可编写跨平台的多线程代码,在众多线程创建方式中,std::thread、std::async 和 std::jthread 是最常用的三种,本文将逐一介绍它们的用法、差异以及选择时机,需要的朋友可以参考下

1. 引言

从 C++11 开始,标准库正式引入了线程支持,让开发者无需依赖平台特定的 API(如 Windows 的 CreateThread 或 POSIX 的 pthread_create)即可编写跨平台的多线程代码。

在众多线程创建方式中,std::thread、std::async 和 std::jthread 是最常用的三种。它们各有特点,适用于不同的场景。本文将逐一介绍它们的用法、差异以及选择时机,帮助你在实际项目中做出合适的选择。

2. std::thread:最基础的线程类

2.1 基本用法

构造函数原型

// C++11 起
template<class Function, class... Args>
explicit thread(Function&& f, Args&&... args);

// 移动构造(C++11 起)
thread(thread&& other) noexcept;

std::thread 是 C++11 引入的线程类,它直接创建一个操作系统线程,并在该线程上执行指定的可调用对象(函数、函数对象、Lambda 表达式等)。

#include <iostream>
#include <thread>

void hello() {
    std::cout << "Hello from thread, id = " << std::this_thread::get_id() << std::endl;
}

int main() {
    std::thread t(hello);   // 创建线程并启动
    t.join();               // 等待线程结束
    return 0;
}

也可以传递参数:

#include <iostream>
#include <thread>

void print_sum(int a, int b) {
    std::cout << "Sum = " << (a + b) << std::endl;
}

int main() {
    std::thread t(print_sum, 3, 5);
    t.join();
    return 0;
}

2.2 关键注意事项

#include <iostream>
#include <thread>

int main() {
    std::thread t([] {
        try {
            // 可能抛出异常的代码
            throw std::runtime_error("error");
        } catch (const std::exception& e) {
            std::cerr << "Caught: " << e.what() << std::endl;
        }
    });
    t.join();
    return 0;
}

2.3 适用场景

3. std::async:异步任务与返回值

3.1 基本用法

函数原型

// C++11 起
template<class F, class... Args>
future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
async(F&& f, Args&&... args);

// C++11 起
template<class F, class... Args>
future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
async(launch policy, F&& f, Args&&... args);

std::async 是 C++11 引入的异步任务工具。它接受一个可调用对象,返回一个 std::future 对象,通过 future.get() 可以获取任务的返回值或异常。

#include <iostream>
#include <future>

int compute(int x) {
    return x * x;
}

int main() {
    std::future<int> fut = std::async(std::launch::async, compute, 7);
    int result = fut.get();   // 阻塞直到任务完成,获取返回值
    std::cout << "Result = " << result << std::endl;
    return 0;
}

3.2 启动策略

std::async 支持两种启动策略:

策略含义
std::launch::async强制在新线程上异步执行
std::launch::deferred延迟执行,在调用 get() 或 wait() 时才同步执行
std::launch::async | std::launch::deferred默认策略,由标准库实现决定
// 强制异步执行
auto fut1 = std::async(std::launch::async, task);

// 延迟执行(惰性求值)
auto fut2 = std::async(std::launch::deferred, task);

// 默认策略(实现决定)
auto fut3 = std::async(task);

3.3 关键注意事项

3.4 适用场景

4. std::jthread:自动 join 的现代线程

4.1 基本用法

构造函数原型

// C++20 起
template<class Function, class... Args>
explicit jthread(Function&& f, Args&&... args);

// C++20 起:接受停止令牌版本
template<class Function, class... Args>
explicit jthread(Function&& f, Args&&... args);

// 移动构造(C++20 起)
jthread(jthread&& other) noexcept;

// 拷贝构造被删除(不可复制)
jthread(const jthread&) = delete;

std::jthread 是 C++20 引入的线程类,全称是 “joining thread”。它的核心优势是析构时自动调用 join(),避免了忘记 join() 导致程序终止的问题。

#include <iostream>
#include <thread>

int main() {
    std::jthread t([] {
        std::cout << "Hello from jthread" << std::endl;
    });
    // 无需手动调用 join(),析构时自动 join
    return 0;
}

4.2 协作式取消

std::jthread 还引入了**停止令牌(stop token)**机制,支持协作式取消。通过 std::stop_token,可以在外部请求线程停止,线程内部可以响应这个请求。

#include <iostream>
#include <thread>
#include <chrono>

int main() {
    std::jthread t([](std::stop_token st) {
        while (!st.stop_requested()) {
            std::cout << "Working..." << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        std::cout << "Stopped." << std::endl;
    });

    std::this_thread::sleep_for(std::chrono::seconds(1));
    t.request_stop();   // 请求停止
    return 0;           // 析构时自动 join
}

4.3 关键注意事项

4.4 适用场景

5. 三种方式的对比

特性std::threadstd::asyncstd::jthread
引入版本C++11C++11C++20
获取返回值不支持(需手动共享状态)支持(通过 future)不支持(需手动共享状态)
自动 join否(需手动调用)不适用是(析构时自动)
协作式取消不支持不支持支持(stop_token)
生命周期控制完全手动由 future 管理自动 join
异常传递线程内需自行处理通过 future 传递线程内需自行处理
适用场景精细控制线程异步任务 + 返回值安全便捷的后台任务

6. 选择时机建议

6.1 优先选择 std::jthread(C++20 项目)

如果你的项目使用 C++20,且不需要获取返回值,优先使用 std::jthread。它自动 join() 的特性极大降低了因忘记 join() 导致程序崩溃的风险,内置的停止令牌也让线程的优雅退出变得简单。

std::jthread worker([](std::stop_token st) {
    while (!st.stop_requested()) {
        // 处理任务
    }
});

6.2 需要返回值时选择 std::async

如果任务需要返回结果,或者希望异常能安全地传递到主线程,std::async 是最简洁的选择。它把线程管理和结果获取封装在一起,代码更清晰。

std::future<int> fut = std::async(std::launch::async, [] {
    return heavy_computation();
});
int result = fut.get();

6.3 需要精细控制时选择 std::thread

当需要精确控制线程的创建时机、生命周期、分离策略,或者需要管理线程池、设置线程优先级等底层操作时,std::thread 仍然是最灵活的选择。但务必记得调用 join() 或 detach()。

std::thread t(worker);
// 精细控制:决定何时 join 或 detach
t.join();

7. 总结

在实际开发中,优先考虑 std::jthread(C++20 项目)或 std::async(需要返回值),只有在需要精细控制时才使用 std::thread。合理选择线程创建方式,能让代码更安全、更简洁、更易维护。

以上就是C++创建线程的几种方法详解的详细内容,更多关于C++线程创建方法的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文