在 C++98/03 标准中,C++ 语言并没有原生支持多线程。虽然可以使用操作系统提供的线程库(如 POSIX 线程库、Windows API 等),但这些并没有标准化到 C++ 语言本身。因此,开发者通常依赖于平台特有的多线程实现,导致了可移植性和一致性的问题。
C++11 标准通过引入新的头文件 <thread>
和多个相关的并发功能来正式支持多线程编程。本文主要介绍了线程使用的基本内容。主要包括:
std::thread
:较为低级的线程使用方式std::packaged_task
、std::future
:用于获取异步操作的结果std::async
:更简单的线程使用方式
- 线程创建 {#title-0} ==================
std::thread
是 C++11 引入的标准库中的一个类,提供了线程的基本功能,使得程序能够在多个线程中并发执行任务。
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
void task(int count)
{
for (int i = 0; i < count; ++i)
{
std::cout << "线程正在运行:" << i + 1 << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void test()
{
// 创建线程,并设置任务函数
std::thread t(task, 5);
// 线程的 joinable() 状态如果是 true,则程序结束后会报错
// 1. joinable() 为 true, 表示状态尚未设置
// 2. 程序结束之后,会调用 thread 析构函数,如果 joinable() 为 true,则会调用 terminate 函数
// 3. 可以通过调用 join() 或者 detach() 函数来修改 joinable() 的状态为 false, 表示已经设置线程状态
// std::cout << "" << std::boolalpha << t.joinable() << std::endl;
// std::this_thread::sleep_for(std::chrono::seconds(10));
// join()
// 1. 设置 joinable() 为 false,表示设置过线程状态
// 2. 阻塞主线程,等待子线程结束之后,才能执行后续代码
// t.join();
// std::cout << "" << std::boolalpha << t.joinable() << std::endl;
// detach()
// 1. 设置 joinable() 为 false,表示设置过线程状态
// 2. 不阻塞主线程,不需要等待子线程结束,可继续执行后续代码
t.detach();
// std::cout << "" << std::boolalpha << t.joinable() << std::endl;
// 关于 detach 补充:
// 1. 无论子线程是 join 还是 detach,主线程和子线程同属于同一个进程空间,共享同一个进程空间资源
// 2. 主线程正常 return 返回,先于子线程结束,即使子线程调用 detach,也会导致子线程提前终止执行
// 3. 子线程 detach,尽量保证该子线程正常执行结束,否则可能会出现一些资源未正确、及时清理的问题
// 4. 如果子线程已经设置为 join, 再设置 detach 会导致程序报错,反之亦然。
std::cout << "程序结束" << std::endl;
}
int main()
{
test();
return EXIT_SUCCESS;
}
#endif
- 线程结果 {#title-1} ==================
std::packaged_task
是 C++11 中引入的一个模板类,它将一个可调用对象(如函数、Lambda 表达式或函数对象)与一个 std::future
绑定在一起,并在稍后通过 std::future
获取任务的执行结果。
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
int task(int number)
{
std::this_thread::sleep_for(std::chrono::seconds(5));
//throw std::exception("抛出异常!");
return 100 + number;
}
void test01()
{
// packaged_task 封装任务函数,确保任务执行完毕时能够提供结果或异常信息。
std::packaged_task<int(int)> current_task(task);
// std::future 是用于获取异步任务结果的对象
// 在线程完成时,std::future 可以获取到该任务的返回值或异常信息
std::future<int> result = current_task.get_future();
// packaged_task 不支持赋值和拷贝构造,支持移动赋值和构造
std::thread t(std::move(current_task), 200);
// t.join();
t.detach();
// 线程函数可能正确执行返回结果,也可能会抛出异常,需要使用 try 块来处理
try
{
int ret = result.get();
std::cout << "ret = " << ret << std::endl;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
}
void test02()
{
std::packaged_task<int(int)> current_task(task);
std::future<int> result = current_task.get_future();
std::thread t(std::move(current_task), 200);
t.detach();
while (true)
{
// 等待指定时间
//std::future_status status = result.wait_for(std::chrono::seconds(1));
// 等待到指定的时间点
auto timeout_time = std::chrono::steady_clock::now() + std::chrono::seconds(1);
std::future_status status = result.wait_until(timeout_time);
// 表示异步任务已经完成,无论是正常完成,还是抛出了异常,结果已经可以通过 get() 获取
if (status == std::future_status::ready)
{
try
{
std::cout << result.get() << std::endl;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
break;
}
// 表示等待异步任务的过程中超时了。也就是说,在调用 wait_for() 或 wait_until() 时,任务没有在指定的时间内完成。
if (status == std::future_status::timeout)
{
std::cout << "超时,可以暂时做其他事情!" << std::endl;
}
}
std::cout << "程序结束" << std::endl;
}
int main()
{
test02();
return EXIT_SUCCESS;
}
#endif
- 高级接口 {#title-2} ==================
std::thread
是 C++ 中一个低级的并发工具,我们可以通过创建 std::thread
对象并传递一个函数来启动新的线程,每个线程必须调用 join()
或 detach()
来确保其正确结束,否则程序可能会发生未定义行为。另外,std::thread
本身确实没有直接支持返回值的机制,需要借助其他的工具(例如 std::packaged_task
)来实现。这样的使用方式可能有些复杂。
std::async
提供了一个更高级的接口,允许你异步执行任务,不需要手动调用 join()
或 detach()
,它会自动返回一个 std::future
对象,这个对象允许你等待并获取任务的结果。
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
int task(int number)
{
std::cout << "线程在执行..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
return 100 + number;
}
void test01()
{
// std::launch::async
// 会创建一个新的线程来执行任务。执行任务不依赖于当前线程,任务会并行执行。
// 适合那些计算量较大、耗时较长的任务,比如大规模数据处理、复杂的数学计算等任务可以采用此策略启动异步执行。
// 下面代码执行完成之后,线程函数就开始执行
std::future<int> result = std::async(std::launch::async, task, 10);
// 在 get 或 wait 执行之前,线程函数已开始执行
std::this_thread::sleep_for(std::chrono::seconds(5));
try
{
int ret = result.get();
std::cout << "ret = " << ret << std::endl;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
std::cout << "程序结束" << std::endl;
}
void test02()
{
// std::launch::deferred
// 任务在当前线程中执行,不会创建新的线程。任务会在调用结果时同步执行。
// 适合那些不一定马上需要结果,或者执行时机可以灵活把控的任务,更灵活地控制任务的执行时间。
std::future<int> result = std::async(std::launch::deferred, task, 10);
// 在 get 或 wait 执行之前,线程函数尚未执行
std::this_thread::sleep_for(std::chrono::seconds(5));
int ret = result.get();
std::cout << "ret = " << ret << std::endl;
}
int main()
{
test01();
return EXIT_SUCCESS;
}
#endif