• 【C++11 多线程】future与promise(八)


    一、C++11为什么要引入std::future?

    我们经常会遇到需要从线程中返回异步任务结果的情况。例如在程序中,我们创建了一个压缩给定文件夹的线程,并且我们希望该线程能够返回新的 zip 文件的名称和大小。

    在 C++11 之前的老方法是使用指针在线程间共享数据:

    • 传递一个指针到新的线程中,该线程将在其中设置数据。直到主线程继续等待使用条件变量。当新线程设置数据并通知条件变量时,主线程将唤醒并从该指针处获取数据。
    • 为了实现这一简单功能,我们使用了一个条件变量、一个 mutex 锁和一个指针,来实现捕获返回值。
    #include<iostream>
    #include<thread>
    #include<mutex>
    
    void fun(int x, int y, int* ans) {
    	*ans = x + y;
    }
    
    int main()
    {
    	int a = 10;
    	int b = 8;
    
    	int* sum = new int(0);
    	std::thread t(fun, a, b, sum);
    	t.join();
    
    	// 获取线程的"返回值"
    	std::cout << *sum << std::endl; // 输出:18
    	delete sum;
        
    	system("pause");
    	return 0;
    }
    

    可以看到还需要使用外部变量,比较麻烦。如果我们想要该线程在不同的时间点返回 3 个不同的值,问题会变得更加复杂,有没有一种简单的方法来从线程处获取返回值呢?答案是 使用std::future

    二、std::future介绍

    C++11 提供了std::future类模板,future 对象提供访问异步操作结果的机制,很轻松解决从异步任务中返回结果。

    在 C++ 标准库中,有两种“期望”,使用两种类型模板实现(这里主要介绍的是唯一期望):

    • 唯一期望(unique futuresstd::future<>) 实例只能与一个指定事件相关联。
    • 共享期望(shared futures, std::shared_future<>) 实例能关联多个事件。

    事实上,一个std::future对象在内部存储一个将来会被某个 provider 赋值的值,并提供了一个访问该值的机制,通过get()成员函数实现。但如果有人试图在get()函数可用之前通过它来访问相关的值,那么get()函数将会阻塞,直到该值可用。

    一个有效的std::future对象通常由以下三种 Provider 创建,并和某个共享状态相关联。Provider 可以是函数或者类,他们分别是:

    • std::async函数,本文后面会介绍
    • std::promise::get_future,get_future 为 promise 类的成员函数
    • std::packaged_task::get_future,此时 get_future为 packaged_task 的成员函数

    C++11 提供的 <future> 头文件中包含了以下几个类和函数:

    • Providers 类:std::promise, std::package_task
    • Futures 类:std::future, shared_future.
    • Providers 函数:std::async()
    • 其他类型:std::future_error, std::future_errc, std::future_status, std::launch

    三、std::future构造函数

    std::future一般由上面三种 Provider 创建,不过也提供了构造函数:

    // default
    future() noexcept;
    // copy [deleted] 
    future (const future&) = delete;
    // move
    future (future&& x) noexcept;
    

    不过std::future的拷贝构造函数是被禁用的,只提供了默认的构造函数和 move 构造函数(注:C++ 新特性)。另外,std::future的普通赋值操作也被禁用,只提供了 move 赋值操作。如下代码所示:

    std::future<int> fut;           // 默认构造函数
    fut = std::async(do_some_task);   // move-赋值操作。
    

    四、std::future成员函数

    其成员函数如下:

    • std::future::valid()

      检查 future 对象是否拥有共享状态,参照构造函数只有两种可用,由默认构造函数创建的 future 对象显然不具有共享状态,即valid()=false,除非它被 move 赋值过;而移动构造函数创建的 future 对象往往拥有共享状态,只不过是否可以立即调用 get() 访问还需要确认共享状态标志是否已被设置为 ready。

    • std::future::get()

      阻塞式获得共享状态的值,如果 future 对象调用 get() 时,共享状态标志尚未被设置为 ready,那么本线程将阻塞至其变为 ready。

    • std::future::wait()

      等待共享状态标志变为 ready,在此之前线程将会一直阻塞。

    • std::future::wait_for()

      与 wait() 不同,wait_for() 只会允许为此等待一段时间 _Rel_time,耗尽这个时间共享状态标志仍不为 ready,wait_for() 一样会返回。

    • std::future::wait_until()

      与 wait_for() 类似的逻辑,只不过 wait_until() 参考的是绝对时间点。到达时间点 _Abs_time 的时候,wait_until() 就会返回,如果没等到 ready 的话,wait_until 一样会返回。

    • std::future::share()

      返回一个 std::shred_future 对象,调用该函数之后,future 对象不和任何共享状态关联,也就不再是 valid 的了。

    其中std::future::wait_for()std::future::wait_until()的返回值如下:

    • future_status::ready:共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常。
    • future_status::timeout:超时,即在规定的时间内共享状态的标志没有变为 ready。
    • future_status::deferred:共享状态包含一个 deferred 函数。

    上面只是对各函数的简单介绍,具体示例请参考:C++11 并发指南四( 详解三 std::future & std::shared_future)

    五、std::promise介绍

    std::promise的作用就是提供一个不同线程之间的数据同步机制,它可以存储一个某种类型的值,并将其传递给对应的 future, 即使这个 future 与 promise 不在同一个线程中也可以安全的访问到这个值。

    可以通过get_future()来获取与该 promise 对象相关联的 future 对象,调用该函数之后,两个对象共享相同的共享状态(shared state)。set_value()可以设置共享状态的值,此后 promise 的共享状态标志变为 ready。

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

    更多内容请参考:C++11 中std::promise 介绍

    六、future与promise配合使用示例

    下面看一个 future 与 promise 配合使用的示例:

    #include<iostream>
    #include<thread>
    #include<mutex>
    #include<atomic>
    #include<future>  //std::future std::promise
    
    void fun(int x, int y, std::promise<int>& promiseObj) {
    	promiseObj.set_value(x + y);
    }
    
    int main()
    {
    	int a = 10;
    	int b = 8;
    
    	// 声明一个promise类
    	std::promise<int> promiseObj;
    	// 将future和promise关联
    	std::future<int> futureObj = promiseObj.get_future();
    	// 模板传参的时候使用ref,否则传参失败
    	std::thread t(fun, a, b, std::ref(promiseObj));
    	t.join();
        
    	// 获取线程的"返回值"
    	int sum = futureObj.get();
    	std::cout << "sum=" << sum << std::endl; // 输出:18
    
    	std::system("pause");
    	return 0;
    }
    

    参考:

    c++11多线程编程(八):std::future , std::promise和线程的返回值

    C++多线程获取返回值方法详解


  • 相关阅读:
    Python疑难杂症:SyntaxError: NonASCII character Python中文处理问题
    程序员健康大全透视身体24小时工作时间表
    ConnectionTimeout,CommandTimeout和executionTimeout的理解
    google map api 与jquery结合使用(3) 图标样式,使用xml和异步请求【转帖】
    新手8周跑步训练计划
    57商城温州地区最大本土网上超市
    7 款仿照 Sinatra 思路的 .NET 框架
    线程池的原理和连接池的原理
    免费网站模版:一个黑色系的公司网站模版(flash幻灯)
    深入浅出REST
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/14577275.html
Copyright © 2020-2023  润新知