std::future和std::promise

1.简介

C++11标准库提供了std::future和std::promise用于处理异步任务。std::future表示期望值,可以使用期望值等待一次性事件。std::promise表示承诺的意思,std::future可以和std::promise进行绑定。std::promise<T>提供设定值的方式(类型为T),这个类型和std::future<T>对象相关联,期望值可以阻塞等待线程,同时,提供数据的线程可以使用组合中的承诺值来对相关值进行设置,并且将期望值的状态设置为就绪状态。

2.std::future

2.1 std::future::get()

std::future::get()是用来获取std::future对象的值或者异常。如果异步任务还未执行完成,get()会阻塞当前线程,直到任务完成,如果任务已经完成,则get()会立即返回。get()函数只能调用一次,它会移动或消耗掉 std::future对象的状态。一旦 get()被调用,std::future 对象就不能再被用来获取结果。

2.2 std::future::wait()

std::future::wait() 调用的时候同样会阻塞当前线程,但是不会得到任务的返回结果,仅仅等待异步任务完成,异步任务完成会立即返回,wait()可以多次调用, 不会消耗std::future对象的状态

2.3 示例

2.3.1 std::future::get()

使用std::async可以启动一个异步任务,跟std::thread对象等待的方式不同,std::async会返回一个std::future<T>对象,这个对象包含异步任务指向的结果(异步任务执行函数的返回值),当需要这个结果的时候,调用std::future对象的get()函数即可,get()函数阻塞线程,直到期望值状态变为就绪。

int do_async_thing(int nData)
{
    std::cout << "thread_id:" << std::this_thread::get_id() <<std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return nData * nData;
}

int main()
{
    //std::future保存的int类型的数据,该值对应do_async_thing任务的返回值
    std::future<int> _future = std::async(std::launch::async,do_async_thing,10);
    //调用get()会阻塞线程,直到执行完do_async_thing
    std::cout << _future.get() << std::endl; //结果为100
    //异步任务执行完成后才会输出主线程的id
    std::cout << "main_id:" << std::this_thread::get_id() << std::endl;

    return 0;
}

2.3.2 std::future::wait()

可以使用wait()去等待任务执行完成,等到执行完成后,然后直接调用get()去获取任务执行的结果

int do_async_thing(int nData)
{
    std::cout << "thread_id:" << std::this_thread::get_id() <<std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return nData * nData;
}

int main()
{
    //std::future保存的int类型的数据,该值对应do_async_thing任务的返回值
    std::future<int> _future = std::async(std::launch::async,do_async_thing,10);
    //调用wait()会阻塞线程,直到执行完do_async_thing
    _future.wait();
    std::cout << _future.get() << std::endl; //结果为100
    //异步任务执行完成后才会输出主线程的id
    std::cout << "main_id:" << std::this_thread::get_id() << std::endl;

    return 0;
}

2.3.3 std::future::wait_for()

除了使用get()和wait()来检测异步任务是否完成,也可以通过wait_for()或者wait_until()来检测在一定的时间内,异步任务是否已经完成,在一些对时序要求较高的程序中,会通过wait_for等来检测任务是否在规定时间完成,如果完成了就正常往下执行,如果没有则走异常流程,确保整个流程执行的时间满足时序要求。

使用wait_for()或者wait_until()方法会返回一个状态std::future_status,当状态为就绪(ready)的时候,表示任务已经完成,如果状态为超时(timeout)的时候,表示在规定时间内未完成任务

上述代码改成wait_for()来等待完成

int do_async_thing(int nData)
{
    std::cout << "thread_id:" << std::this_thread::get_id() <<std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return nData * nData;
}

int main()
{

    std::future<int> _future = std::async(std::launch::async,do_async_thing,10);
    std::future_status _status =  _future.wait_for(std::chrono::milliseconds(500));
    //输出timeout,任务在500ms之内未完成
    if (_status == std::future_status::timeout)
    {
        std::cout << "timeout " << std::endl;
    }
    //如果在规定的时间内完成了任务则状态会变成ready
    else if(_status == std::future_status::ready)
    { 
        std::cout << _future.get() << std::endl;
    }
    std::cout << "main_id:" << std::this_thread::get_id() << std::endl;

    return 0;
}

2.3.4 std::future::wait_until()

wait_until的用法跟wait_for()用法类似,将上述代码改成wait_until()的形式

int do_async_thing(int nData)
{
    std::cout << "thread_id:" << std::this_thread::get_id() <<std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return nData * nData;
}

int main()
{

    std::future<int> _future = std::async(std::launch::async,do_async_thing,10);
    //获取当前时间点
    auto start = std::chrono::steady_clock::now();
    //从当前时间点后的500ms
    auto end = start + std::chrono::milliseconds(500);
    //等待异步任务完成或者达到end的时间节点
    std::future_status _status = _future.wait_until(end);
    //输出timeout
    if (_status == std::future_status::timeout)
    {
        std::cout << "timeout " << std::endl;
    }
    else if(_status == std::future_status::ready)
    { 
        std::cout << _future.get() << std::endl;
    }
    std::cout << "main_id:" << std::this_thread::get_id() << std::endl;

    return 0;
}

3.std::promise

std::promise可以在某一线程中去设置值或者异常,std::future在另一个线程可以去或者这个值或者异常。可以通过给定的一个std::promise的get_future()来获取与之相关的std::future对象。

3.1 std::set_value

std::promise可以通过set_value来设置promise的值,然后可以通过std::future的get()来获取设置的这个值,调用get()获取值的时候,如果promise的值还未被设置,则会阻塞当前线程。

int do_some_thing(std::promise<int> promise)
{
    int nData = 100;
    //设置promise的值
    promise.set_value(nData);
    return nData;
}

int main()
{
    
    //创建一个promise对象
    std::promise<int> _promise;
    //创建一个std::future对象,该对象与_promise对象关联
    std::future<int> _future = _promise.get_future();

    std::thread t(do_some_thing,std::move(_promise));
    //获取_promise设置的值,结果输出100
    std::cout << _future.get() << std::endl;

    t.join();

    return 0;
}

3.2 set_exception

除了通过set_value来设置promise的值,也可以通过set_exception来设置异常值

void set_exception(std::promise<void> promise)
{
    try 
    {
        //抛出异常,runtime_error表示只有在运行时才能检测的的问题
        //继承自std::exception
        throw runtime_error("run error");
    }
    catch (...)
    {
        //current_exception:当前正在传播的异常
        promise.set_exception(std::current_exception());
    }
}

int main()
{

    std::promise<void> _promise;
    std::future<void> _future = _promise.get_future();

    std::thread t(set_exception,std::move(_promise));
    //获取异常
    try 
    {
        std::cout << "wait" << std::endl;
        _future.get();
    }
    catch (const std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }

    t.join();
    //输出wait然后再出书run error
    

    return 0;
}

 4.std::future和std::promise示例

两个模块,模块1生产数据,然后将数据传递给模块2,等待模块2在规定时间内完成,然后返回结果,示例如下:

struct Data;

std::queue<std::shared_ptr<Data>> g_queue;
std::mutex g_mtx;
std::condition_variable g_cv;

struct Data
{
    Data()
    {
        m_sPtr = std::make_shared<std::promise<int>>();
    }

    Data(const Data& other)
    {
        m_sPtr = other.m_sPtr;
    }

    Data &operator = (const Data &other)
    {
        if (this != &other)
        {
            m_sPtr = other.m_sPtr;
        }

        return *this;
    }
 
    std::shared_ptr<std::promise<int>> m_sPtr;
};

void Producer()
{
    while (true)
    {
        //创建数据
        auto data =  std::make_shared<Data>();
        //将std::future对象和std::promise绑定,通过promise的get_future来获取
        auto future = data->m_sPtr->get_future();
        {
            //数据塞入队列
            std::unique_lock<std::mutex> locker(g_mtx);
            g_queue.push(data);
            g_cv.notify_one();
        }
         
        //再规定时间内等待结果
        auto status =  future.wait_for(std::chrono::milliseconds(10));
        if (std::future_status::timeout == status)
        {
            std::cout << "timeout" << std::endl;
            break;
        }
        else if (std::future_status::ready == status)
        {
            std::cout << "value:" << future.get() << std::endl;
        }

    }
}

void Consumer()
{
    static int s_nCount = 1;
    while (true)
    {
        
        std::shared_ptr<Data> data;
        {
            std::unique_lock<std::mutex> locker(g_mtx);
            g_cv.wait(locker, [&]() 
                {
                    return !g_queue.empty();
                });
            data = g_queue.front();
            g_queue.pop();
        }
        //设置值
        data->m_sPtr->set_value(s_nCount++);
    }
}

int main()
{

    std::thread t1(Producer);
    std::thread t2(Consumer);

    t1.join();
    t2.join();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值