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

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

一、std::promise介绍

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

std::future 可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段。std::future 通常由某个 Provider 创建,你可以把 Provider 想象成一个异步任务的提供者,Provider 在某个线程中设置共享状态的值,与该共享状态相关联的 std::future 对象调用 get(通常在另外一个线程中) 获取该值,如果共享状态的标志不为 ready,则调用 std::future::get 会阻塞当前的调用者,直到 Provider 设置了共享状态的值(此时共享状态的标志变为 ready),std::future::get 返回异步任务的值或异常(如果发生了异常)。

二、代码示例:

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

示例1:

#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的数据,那么可以保存函数指针吗?答案是可行的,请看示例。

示例2:

#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;
}

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

示例3:

#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;
}
相关文章
|
22天前
|
存储 对象存储 C++
C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
本文深入对比了 C++ 标准库中的 `std::array` 和 `std::vector`,从内存管理、性能、功能特性、使用场景等方面详细分析了两者的差异。`std::array` 适合固定大小的数据和高性能需求,而 `std::vector` 则提供了动态调整大小的灵活性,适用于数据量不确定或需要频繁操作的场景。选择合适的容器可以提高代码的效率和可靠性。
48 0
|
7月前
|
存储 前端开发 安全
C++一分钟之-未来与承诺:std::future与std::promise
【6月更文挑战第27天】`std::future`和`std::promise`是C++异步编程的关键工具,用于处理未完成任务的结果。`future`代表异步任务的结果容器,可阻塞等待或检查结果是否就绪;`promise`用于设置`future`的值,允许多线程间通信。常见问题包括异常安全、多重获取、线程同步和未检查状态。解决办法涉及智能指针管理、明确获取时机、确保线程安全以及检查未来状态。示例展示了使用`std::async`和`future`执行异步任务并获取结果。
139 2
|
4月前
|
安全 C++
C++: std::once_flag 和 std::call_once
`std::once_flag` 和 `std::call_once` 是 C++11 引入的同步原语,确保某个函数在多线程环境中仅执行一次。
|
6月前
|
存储 C++ 运维
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
62 6
|
6月前
|
C++ 运维
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
81 2
|
7月前
|
安全 C++
C++一分钟之-字符串处理:std::string
【6月更文挑战第25天】`std::string`是C++文本处理的核心,存在于`&lt;string&gt;`库中。它支持初始化、访问、连接、查找、替换等操作。常见问题包括空指针解引用、越界访问和不当内存管理。要安全使用,确保字符串初始化,用`at()`检查边界,用`.empty()`检查空字符串,且无需手动释放内存。高效技巧包括预先分配内存、利用互转函数以及使用迭代器。记得正确比较和遍历字符串以保证代码效率和安全性。
83 5
|
7月前
|
存储 设计模式 安全
C++一分钟之-并发编程基础:线程与std::thread
【6月更文挑战第26天】C++11的`std::thread`简化了多线程编程,允许并发执行任务以提升效率。文中介绍了创建线程的基本方法,包括使用函数和lambda表达式,并强调了数据竞争、线程生命周期管理及异常安全等关键问题。通过示例展示了如何用互斥锁避免数据竞争,还提及了线程属性定制、线程局部存储和同步工具。理解并发编程的挑战与解决方案是提升程序性能的关键。
95 3
|
7月前
|
C++
c++中的using namespace std;
c++中的using namespace std;
187 1
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
66 2
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
118 5