zoukankan      html  css  js  c++  java
  • 【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++多线程获取返回值方法详解


  • 相关阅读:
    1058 A+B in Hogwarts (20)
    1046 Shortest Distance (20)
    1061 Dating (20)
    1041 Be Unique (20)
    1015 Reversible Primes (20)(20 分)
    pat 1027 Colors in Mars (20)
    PAT 1008 Elevator (20)
    操作系统 死锁
    Ajax的get方式传值 避免& 与= 号
    让IE浏览器支持CSS3表现
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/14577275.html
Copyright © 2011-2022 走看看