C++ std::packaged_task 工具使用

在 C++ 中,std::thread 用于创建和管理线程,但它并不直接支持获取线程的返回结果。为了解决这个问题,我们可以使用 std::packaged_task 来辅助实现。std::packaged_task 是一个可调用对象包装器,它将一个函数封装起来,并可以与 std::future 结合使用来异步获取函数的结果。

使用步骤:

  1. 封装任务:将需要在线程中执行的函数传给 std::packaged_task
  2. 获取 future 对象:通过 std::packaged_taskget_future() 方法获得一个 std::future 对象。
  3. 启动线程:将 packaged_task 移交给 std::thread 并启动线程。
  4. 获取结果:通过 future 对象的 get() 方法获取线程执行的返回值。

#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 test()
{
    // 1. 封装任务
    std::packaged_task<int(int)> current_task(task);

    // 2. 获取 future 对象
    std::future<int> result = current_task.get_future();

    // 3. 启动线程
    // packaged_task 不支持拷贝,只支持移动
    // std::move 将 current_task 转换为 右值引用,将其所有权转移给 std::thread
    // std::thread 接收到 current_task 的 所有权,并会在内部保存这个对象。
    // std::thread t(std::move(current_task), 200);

    // std::ref 传递的是 current_task 的 引用,线程内操作的是原始对象。
    // std::thread 不会接管 current_task 的生命周期,也不会负责销毁 current_task。
    std::thread t(std::ref(current_task), 200);

    t.detach();


    // 4. 获取结果
    // 线程函数可能正确执行返回结果,也可能会抛出异常,需要使用 try 块来处理
    try
    {
        int ret = result.get();
        std::cout << "ret = " << ret << std::endl;
    }
    catch (const std::exception& ex)
    {
        std::cout << ex.what() << std::endl;
    }
}


int main()
{
    test();
    return EXIT_SUCCESS;
}
#endif

使用 get 函数获得结果会阻塞当前线程。如果我们不想阻塞在此处,可以使用 wait_for 或者 wait_until 来代替 get 函数。

#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));
	return 100 + number;
}


void test()
{
	std::packaged_task<int(int)> current_task(task);
	std::future<int> result = current_task.get_future();
	std::thread t(std::move(current_task), 200);
	

	while (true)
	{
		// 等待指定长度的时间,如果在该时间内子线程没有返回结果,则返回 timeout 状态,否则返回 ready 状态
		// std::future_status status = result.wait_for(std::chrono::seconds(1));
		std::future_status status = result.wait_until(std::chrono::steady_clock::now() + std::chrono::seconds(1));
		if (status == std::future_status::ready)
		{
			std::cout << "线程结果是:" << result.get() << std::endl;
			break;
		}

		if (status == std::future_status::timeout)
		{
			std::cout << "任务正在执行中...做点别的事情" << std::endl;
		}
	}

	t.join();
}


int main()
{
	test();
	return EXIT_SUCCESS;
}
#endif

std::future 只可以获得一次结果,不允许多次获得线程结果。如果需要多次获得线程的结果,可以使用 std::shared_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));
	return 100 + number;
}


void test()
{
	std::packaged_task<int(int)> current_task(task);
#if 0
	std::future<int> result = current_task.get_future();
#else
	std::shared_future<int> result = current_task.get_future();
#endif
	std::thread t(std::move(current_task), 200);
	
	t.join();

	result.get();
	result.get();
}


int main()
{
	test();
	return EXIT_SUCCESS;
}
#endif
未经允许不得转载:一亩三分地 » C++ std::packaged_task 工具使用
评论 (0)

6 + 5 =