std::promise介绍及使用

[C++11]std::promise介绍及使用

一、std::promise介绍

std::promise (#include <future>)是C++11并发编程中常用的一个类,常配合std::future使用。其作用是在一个线程t1中保存一个类型typename T的值,可供相绑定的std::future对象在另一线程t2中获取。

二、代码示例:

下面我们通过几个简单小例子逐渐深入了解std::promise的使用方法。

#include <iostream>
#include <future>
#include <chrono>
 
void Thread_Fun1(std::promise<int> &p)
{
    //为了突出效果,可以使线程休眠5s
    std::this_thread::sleep_for(std::chrono::seconds(5));
    int iVal = 233;
    std::cout << "传入数据(int):" << iVal << std::endl;
    //传入数据iVal
    p.set_value(iVal);
}
 
void Thread_Fun2(std::future<int> &f)
{
    //阻塞函数,直到收到相关联的std::promise对象传入的数据
    auto iVal = f.get();        //iVal = 233
    std::cout << "收到数据(int):" << iVal << std::endl;
}
 
int main()
{
    //声明一个std::promise对象pr1,其保存的值类型为int
    std::promise<int> pr1;
    //声明一个std::future对象fu1,并通过std::promise的get_future()函数与pr1绑定
    std::future<int> fu1 = pr1.get_future();
    //创建一个线程t1,将函数Thread_Fun1及对象pr1放在线程里面执行
    std::thread t1(Thread_Fun1, std::ref(pr1));
    //创建一个线程t2,将函数Thread_Fun2及对象fu1放在线程里面执行
    std::thread t2(Thread_Fun2, std::ref(fu1));
    //阻塞至线程结束
    t1.join();
    t2.join();
    return 1;
}

可以看到std::future对象fu1先是通过std::promise的函数get_future()与std::promise对象pr1相绑定,pr1在线程t1中通过set_value()传入共享数据,fu1在线程t2中通过阻塞函数get()获取到传入的数据。
示例1中传入的数据类型是int,前面介绍中说std::promise可以保存typename T的数据,那么可以保存函数指针吗?答案是可行的,请看示例。

#include <iostream>
#include <future>
#include <chrono>
#include <functional>
 
//声明一个可调对象T
using T = std::function<int(int)>;        //等同于typedef 			       //std::function<int(int)> T;
int Test_Fun(int iVal)
{
    std::cout << "Value is:" << iVal << std::endl;
    return iVal + 232;
}
 
void Thread_Fun1(std::promise<T> &p)
{
    //为了突出效果,可以使线程休眠5s
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "传入函数Test_Fun" << std::endl;
    //传入函数Test_Fun
    p.set_value(std::bind(&Test_Fun, std::placeholders::_1));
}
 
void Thread_Fun2(std::future<T> &f)
{
    //阻塞函数,直到收到相关联的std::promise对象传入的数据
    auto fun = f.get();        //iVal = 233
    int iVal = fun(1);
    std::cout << "收到函数并运行,结果:" << iVal << std::endl;
}
 
int main()
{
    //声明一个std::promise对象pr1,其保存的值类型为int
    std::promise<T> pr1;
    //声明一个std::future对象fu1,并通过std::promise的get_future()函数与pr1绑定
    std::future<T> fu1 = pr1.get_future();
    //创建一个线程t1,将函数Thread_Fun1及对象pr1放在线程里面执行
    std::thread t1(Thread_Fun1, std::ref(pr1));
    //创建一个线程t2,将函数Thread_Fun2及对象fu1放在线程里面执行
    std::thread t2(Thread_Fun2, std::ref(fu1));
    //阻塞至线程结束
    t1.join();
    t2.join();
    return 1;
}

既然可以传函数对象,那么是否可以通过模板魔改,传入可变元函数?请看示例。

#include <iostream>
#include <future>
#include <chrono>
#include <functional>
 
//声明一个可调对象F
using F = std::function<int(int, int, int&)>;        //等同于typedef std::function<int(int, int, int&)> F;
 
//函数可以改成任意参数,任意返回类型
int Test_Fun(int a, int b, int &c)
{
    //a = 1, b = 2
    c = a + b + 230;
    return c;
}
 
void Thread_Fun1(std::promise<F> &p)
{
    //为了突出效果,可以使线程休眠5s
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "传入函数Test_Fun" << std::endl;
    //传入函数Test_Fun
    p.set_value(std::bind(&Test_Fun, std::placeholders::_1,        std::placeholders::_2, std::placeholders::_3));
}
 
template<typename T, typename ...Args>
void Thread_Fun2(std::future<T> &f, Args&& ...args)
{
    //阻塞函数,直到收到相关联的std::promise对象传入的数据
    auto fun = f.get();        //fun等同于Test_Fun
    auto fResult = fun(std::forward<Args>(args)...);
    std::cout << "收到函数并运行,结果:" << fResult << std::endl;
}
 
int main()
{
    //声明一个std::promise对象pr1,其保存的值类型为int
    std::promise<F> pr1;
    //声明一个std::future对象fu1,并通过std::promise的get_future()函数与pr1绑定
    std::future<F> fu1 = pr1.get_future();
    //声明一个变量
    int iVal = 0;
    //创建一个线程t1,将函数Thread_Fun1及对象pr1放在线程里面执行
    std::thread t1(Thread_Fun1, std::ref(pr1));
    //创建一个线程t2,将函数Thread_Fun2及对象fu1放在线程里面执行
    std::thread t2(Thread_Fun2<F, int, int, int&>, std::ref(fu1), 1, 2, std::ref(iVal));
 
    //阻塞至线程结束
    t1.join();
    t2.join();
    //此时iVal的值变成233
    return 1;
}

三、其他

  • 其实,std::promise<void>也是合法的。此时std::promise.set_value不接受任何参数,仅用于通知关联的std::future.get()解除阻塞。
  • 通过std::promise::set_exception函数可以设置自定义异常,该异常最终会被传递到std::future,并在其get函数中被抛出。
//在线程t1中
 promise.set_exception(std::make_exception_ptr(std::logic_error("caught")));
//在线程t2中
void catch_error(std::future<void> &future) {
    try {
        future.get();
    } catch (std::logic_error &e) {
        std::cerr << "logic_error: " << e.what() << std::endl;
    }
}
  • std::promise<T>多用于线程同步,与条件变量相比,有以下几点优势:

    (1)条件变量存在虚假唤醒

    (2)条件变量需要与**std::unique_lock<std::mutex>**配合使用;

    (3)此外,两者都会阻塞线程,进入闲等待,不占用CPU资源。

WOW, Funny C++11 !

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值