【C++11多线程】异步任务:async、packaged_task、promise

1.std::async()

std::async() 是 C++11 提供的异步任务高级抽象,包含在 <future> 头文件中,可以很方便地实现异步地执行一个任务,并在需要的时候获取其结果。和直接使用 std::thread 相比,有着一些优势:

  1. std::async() 返回的 std::future 对象,可以很方便地等待可调用对象执行完成并获取其返回值。
  2. 能从实现库的一些高级功能中获益,比如线程池等,并大大减少异常的产生。

std::async() 是个函数模板,其源码如下所示。

在这里插入图片描述

传给 std::async() 可以是任何类型的可调用对象,可以是函数、成员函数、函数对象或lambda表达式。

1.1 两种启动策略

在这里插入图片描述

std::async() 有两种启动策略,即 std::launch::async 和 std::launch::deferred。如果不明确指定,则为 std::launch::async | std::launch::deferred。

  • 对于 std::launch::async 来说,std::async() 会自动创建一个新线程并开始执行对应的线程入口函数。

  • 对于 std::launch::deferred 来说,std::async() 不会创建新线程去执行,直到调用 wait() 或 get() 时才在当前线程中去执行可调用对象。

  • 对于 std::launch::async | std::launch::deferred 来说,标准中并没有规定默认采用哪种策略,由实现库自己决定。经测试发现,VS2013 默认为 std::launch::async,而 gcc4.8.2 默认为 std::launch::deferred。

1.1.1 std::launch::async

在下面代码中,std::async() 用来启动一个异步任务,自动创建一个新线程并开始执行对应的线程入口函数,返回一个 std::future 对象,这个 std::future 对象里边就含有线程返回的结果(即线程入口函数所返回的结果),我们可以通过调用 std::future 对象的成员函数 get() 来获取结果。需要注意的是,std::future 的 get() 只能调用一次,不能调用多次。

普通函数:

#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;
    std::future<int> result = std::async(std::launch::async, mythread, 180); // 创建一个新线程并开始执行,流程并不会卡在这里
    cout << "main continue..." << endl;
    cout << result.get() << endl; // 卡在这里等待mythread线程执行完毕,拿到结果
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

类的成员函数:

#include <iostream>
#include <future>
using namespace std;

class A
{
public:
    int mythread(int val) // 线程入口函数
    {
        cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
        cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
        return val;
    }
};

int main()
{
    A a;
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;
    std::future<int> result = std::async(std::launch::async, &A::mythread, &a, 180); // 创建一个新线程并开始执行,流程并不会卡在这里
    cout << "main continue..." << endl;
    cout << result.get() << endl; // 卡在这里等待mythread线程执行完毕,拿到结果
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

1.1.2 std::launch::deferred

普通函数:

#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;
    std::future<int> result = std::async(std::launch::deferred, mythread, 180);
    cout << "main continue..." << endl;
    cout << result.get() << endl; // 在主线程中执行mythread()函数,拿到结果
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

类的成员函数:

#include <iostream>
#include <future>
using namespace std;

class A
{
public:
    int mythread(int val) // 线程入口函数
    {
        cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
        cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
        return val;
    }
};

int main()
{
    A a;
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;
    std::future<int> result = std::async(std::launch::deferred, &A::mythread, &a, 180);
    cout << "main continue..." << endl;
    cout << result.get() << endl; // 在主线程中执行A::mythread()函数,拿到结果
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

2.std::packaged_task

std::packaged_task 是个类模板,它的模板参数是各种可调用对象,其源码如下所示。

在这里插入图片描述

std::packaged_task 包装一个可调用对象,并且允许异步获取该可调用对象产生的结果。从包装可调用对象意义上来讲,std::packaged_task 与 std::function 类似,只不过 std::packaged_task 将其包装的可调用对象的执行结果传递给一个 std::future 对象(该对象通常在另外一个线程中获取 std::packaged_task 任务的执行结果)。

std::packaged_task 对象内部包含了两个最基本元素:

  1. 被包装的任务(stored task),任务(task)是一个可调用的对象,如函数指针、成员函数指针或者函数对象。
  2. 共享状态(shared state),用于保存任务的返回值,可以通过 std::future 对象来达到异步访问共享状态的效果。

可以通过 std::packged_task::get_future() 来获取与共享状态相关联的 std::future 对象。在调用该函数之后,两个对象共享相同的共享状态,具体解释如下:

  • std::packaged_task 对象是异步 Provider,它在某一时刻通过调用被包装的任务来设置共享状态的值。
  • std::future 对象是一个异步返回对象,通过它可以获得共享状态的值,当然在必要的时候需要等待共享状态标志变为 ready。

std::packaged_task 的共享状态的生命周期一直持续到最后一个与之相关联的对象被释放或者销毁为止。

std::packaged_task 不会自己启动,你必须调用它。

#include <iostream>
#include <thread>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;

    std::packaged_task<int(int)> myTask(mythread); // 将mythread()函数包装起来
    
    std::future<int> result = myTask.get_future(); // 从packaged_task获取相关的future
    
    std::thread threadObj(std::move(myTask), 180); // 将packaged_task传递给线程以异步运行
    
    threadObj.join(); // join线程,阻塞直到线程完成时返回
    
    cout << result.get() << endl; // 获取packaged_task的结果,即mythread()函数的返回值
    
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

输出结果如下:

在这里插入图片描述

#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;

    std::packaged_task<int(int)> myTask(mythread); // 将mythread()函数包装起来
    
    std::future<int> result = myTask.get_future(); // 从packaged_task获取相关的future

    myTask(180); // packaged_task本身也是一个可调用对象,可以直接调用

    cout << result.get() << endl; // 获取packaged_task的结果,即mythread()函数的返回值
    
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

输出结果如下:

在这里插入图片描述

3.std::promise

std::promise 是个类模板,其源码如下所示。

在这里插入图片描述

std::promise 对象可以保存某一类型 T 的值,该值可被 std::future 对象读取(可能在另外一个线程中)。具体说来,在 std::promise 对象构造时,可以和一个共享状态(通常是std::future)相关联,并可以在相关联的共享状态(std::future)上保存一个类型为 T 的值。

可以通过 std::promise::get_future() 来获取与该 std::promise 对象相关联的 std::future 对象,调用该函数之后,两个对象共享相同的共享状态,具体解释如下:

  • std::promise 对象是异步 Provider,它可以在某一时刻设置共享状态的值。
  • std::future 对象可以异步返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标志变为 ready,然后才能获取共享状态的值。

我们可以在某个线程中给 std::promise 赋值,然后在其他线程中把这个值取出来使用。

#include <iostream>
#include <thread>
#include <future>
using namespace std;

void mythread1(std::promise<int>& pro, int val)
{
    cout << "mythread1 start, " << "mythread1 id = " << std::this_thread::get_id() << endl;
    val += 100;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 假设有其他运算耗时5秒
    pro.set_value(val);
    cout << "mythread1 end, " << "mythread1 id = " << std::this_thread::get_id() << endl;
    return;
}

void mythread2(std::future<int>& fu)
{
    cout << "mythread2 start, " << "mythread2 id = " << std::this_thread::get_id() << endl;
    auto val = fu.get();
    cout << val << endl;
    cout << "mythread2 end" << "mythread2 id = " << std::this_thread::get_id() << endl;
    return;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;

    std::promise<int> pro;

    std::future<int> fu = pro.get_future();

    std::thread threadObj1(mythread1, std::ref(pro), 80);
    threadObj1.join();

    std::thread threadObj2(mythread2, std::ref(fu));
    threadObj2.join();

    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

输出结果如下:

在这里插入图片描述

4.参考资料

https://blog.csdn.net/lijinqi1987/article/details/78909524

https://www.cnblogs.com/haippy/p/3279565.html

https://www.cnblogs.com/haippy/p/3239248.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值