在多线程编程中,需要数据共享,如一个线程需要使用另一线程运算产生的数据。
涉及到异步编程时,有时需要线程间同步,如执行异步订阅消息时,当订阅消息的结果返回成功后,处理消息的线程才需要运行。
以上场景的都可以使用std::future和std::promise实现
例子1
#include<iostream>
#include<future>
#include<mutex>
#include<chrono>
#include<thread>
#include<stdlib.h>
using namespace std;
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::ref,表示我们想要传递的是变量本身
std::thread t1(Thread_Fun1, std::ref(pr1));
//创建一个线程t2,将函数Thread_Fun2及对象fu1放在线程里面执行
std::thread t2(Thread_Fun2, std::ref(fu1));
//阻塞至线程结束
t1.join();
t2.join();
system("pause");
return 0;
}
执行结果:
传入数据(int):233
收到数据(int):233
多线程共享值
步骤:
- 在一个线程A中创建一个 std::promise 对象:std::promise<int> proObj;
- 把 std::promise 关联到 std::future: std::future<int> futObj = proObj.get_future();
- 把 proObj 对象传递给另一个线程B,线程B会在适当的时刻设置该对象的值:proObj.set_value(23);
- 在线程A中通过 std::future 读取对象的值:int val = futObj.get();
就是这么简单,不管线程B在何时设置对象的值,线程A总能安全地获取。有一个点需要注意,在A中通过futObj.get()获取值时,若B还未设置,则A会阻塞,直到设置了值并成功获取。
例子2
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
// 线程B
void initiazer(std::promise<int> * promObj)
{
std::cout << "Thread B" << std::endl;
// set the value at proper time
std::this_thread::sleep_for(std::chrono::seconds(3));
promObj->set_value(23);
}
int main()
{
// 线程A
std::promise<int> promiseObj;
std::future<int> futureObj = promiseObj.get_future();
std::thread th(initiazer, &promiseObj); // 启动线程B
// std::thread th(initiazer, std::ref(promiseObj)); // 启动线程B 对应上面函数参数就得用&引用
// 获取对象的值,该调用在B设置其值后会返回23,在B设置其值前会阻塞
std::cout<< futureObj.get() << std::endl;
th.join();
return 0;
}
执行结果:
Thread B
23
线程间同步
可以使用如下代码判断共享对象的值是否就绪:
template<typename T>
bool is_ready(std::future<T> const& f)
{ return f.wait_for(std::chrono::seconds(3)) == std::future_status::ready; }
future对象的wait_for函数会阻塞等待结果变得可用,可用的标志为以下两种情况之一:
- 设置的时间超时
- 共享对象的状态变为ready
它的原型如下:
template< class Rep, class Period >
std::future_status wait_for( const std::chrono::duration<Rep,Period>& timeout_duration ) const;
返回值标识了结果的状态,为:
- future_status::deferred :计算结果的函数未启动
- future_status::ready:结果ready
- future_status::timeout:超时
例子3
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
// 线程B
void loginSrv(std::promise<bool> * promObj)
{
std::cout << "Thread B" << std::endl;
// set the value
std::this_thread::sleep_for(std::chrono::seconds(3));
// if (loginSucc)
// promObj->set_value(true);
// else
promObj->set_value(false);
}
int main()
{
// 线程A
std::promise<bool> promiseObj;
std::future<bool> futureObj = promiseObj.get_future();
std::thread th(loginSrv, &promiseObj); // 启动线程B
// 阻塞8s等待登录结果,若8s内未返回结果,则超时
if (futureObj.wait_for(std::chrono::seconds(8)) == std::future_status::ready)
if (futureObj.get()) // 返回登录结果,不会再阻塞
std::cout<<"doLoginSuccThings"<< std::endl;
else
std::cout<<"doLoginFailedThings"<< std::endl;
else
// 超时未登录成功
std::cout<<"doOtherThings"<< std::endl;
th.join();
system("pause");
return 0;
}
执行结果:
Thread B
doLoginFailedThings
小结:
- 使用promise和future能够快速地进行线程间数据共享和同步。
- promise提供值,并在以后改变值(set_value)。
- future关联到promise,线程安全地获取值(get)。
- 利用future的阻塞等待特性(wait_for),可以实现线程同步。