51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

C++ 多线程使用

在 C++98/03 标准中,C++ 语言并没有原生支持多线程。虽然可以使用操作系统提供的线程库(如 POSIX 线程库、Windows API 等),但这些并没有标准化到 C++ 语言本身。因此,开发者通常依赖于平台特有的多线程实现,导致了可移植性和一致性的问题。

C++11 标准通过引入新的头文件 <thread> 和多个相关的并发功能来正式支持多线程编程。本文主要介绍了线程使用的基本内容。主要包括:


  • std::thread:较为低级的线程使用方式
  • std::packaged_taskstd::future:用于获取异步操作的结果
  • std::async:更简单的线程使用方式
  1. 线程创建 {#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
  1. 线程结果 {#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

  1. 高级接口 {#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

赞(0)
未经允许不得转载:工具盒子 » C++ 多线程使用