【C++11多线程入门教程】系列之future(二)

future与shared_future

  上一篇博文介绍关于future的一些基本应用与注意事项,还简单介绍std::shared_future类接收future类模板的share()成员函数移动语义给std::shared_future类对象,可以多次调用get()函数。下面是std::shared_future类模板的相关声明与定义,可以发现有拷贝构造与拷贝运算。

template <class _Ty>
class shared_future : public _State_manager<_Ty> { // class that defines a copyable asynchronous return object
                                                   // that holds a value
    using _Mybase = _State_manager<_Ty>;

public:
    shared_future() noexcept { // construct
    }

    shared_future(const shared_future& _Other) noexcept : _Mybase(_Other) { // construct from shared_future object
    }

    shared_future& operator=(const shared_future& _Right) noexcept { // assign from shared_future object

        _Mybase::operator=(_Right);
        return *this;
    }

    shared_future(future<_Ty>&& _Other) noexcept
        : _Mybase(_STD forward<_Mybase>(_Other)) { // construct from rvalue future object
    }

    shared_future(shared_future&& _Other) noexcept
        : _Mybase(_STD move(_Other)) { // construct from rvalue shared_future object
    }

    shared_future& operator=(shared_future&& _Right) noexcept { // assign from shared_future rvalue object

        _Mybase::operator=(_STD move(_Right));
        return *this;
    }

    ~shared_future() noexcept { // destroy
    }

    const _Ty& get() const { // block until ready then return the stored result or
                             // throw the stored exception
        return this->_Get_value();
    }
};

下面我们对shared_future类模板做个小结:

  • 可以显示or隐式的获取future对象的所有权,调用方法如下:
	std::future<int> fut = std::async(get_value);
	// 1. fut.share()共享之后, fut的内容权限传递给shfut,fut为empty.
    // 2. 传递给共享对象之后,可以多次调用访问.
	//std::shared_future<int> shfut = fut.share(); // 显示获取对象所有权
	std::shared_future<int> shfut(std::move(fut)); // 隐式获取future对象所有权
  • shared_future可以多次调用get()成员函数,是否释放为当其关联的计数为0时候,而future执行get()一次之后,就将所有权转交出去,不能够多次调用。
std::promise的使用

下面我们通过一个示例程序来介绍一下std::promise的功能使用:

#include <iostream>       // std::cout
#include <functional>     // std::ref
#include <thread>         // std::thread
#include <future>         // std::promise, std::future

class Student
{
public:
	Student() {
		age_ = 0;
		name_ = "empty";
		score_ = 0;
	}

	Student(int age, std::string name, float score)
		:age_(age), name_(name), score_(score)
	{}

	~Student() = default;
public:
	int age_;
	std::string name_;
	float score_;
};

// 通过promise来给线程传入参数
void print_int(std::future<Student>& fut)
{
	std::cout << "start thread runs ..." << std::endl;   // ①
	Student stu = fut.get();  // ②
	std::cout <<"name: " << stu.name_ << " age: " << stu.age_ 
		      << " score: " << stu.score_ << std::endl;  // ③
}

int main()
{
	std::promise<Student> prom;                   // create promise
	std::future<Student> fut = prom.get_future(); // engagement with future

	std::thread th1(print_int, std::ref(fut));  // ④

	std::this_thread::sleep_for(std::chrono::seconds(2)); // 2s sleep 
	Student stu(10, "Kai", 99.5);

	prom.set_value(stu);   // ⑥
	th1.join(); // ⑦
	return 0;
}

下面对上述代码进行分析:程序执行流程为:④ → \rightarrow → \rightarrow → \rightarrow → \rightarrow → \rightarrow ③了解了执行流程后,我们来看一下关于上面这个程序要说明的功能:

  • 输入的Student类作为结构体输入future类模板,更加便利使用;future类模板不仅支持原始类型还支持特化模式;
  • 通过使用std::promise类对象的get_future()函数与future对象绑定到一起;
  • 启动线程,但是函数内部传入future调用get()函数需要等待输入参数;
  • 通过std::promise类的set_value()函数从外部设置参数,执行传入到线程函数里面;
  • 线程join()阻塞等待线程函数执行完毕;

  上述一个小程序简短说明std::promise类目标的作用,可以与某个future对象关联,共享某个状态值,通过set_value来进行共享值的传递。

std::packaged_task的使用

  关于std::packaged_task类模板主要是包装可调用的对象,可以是函数指针、成员函数指针、函数对象等;其次是能够保存任务的返回值,共享状态机制通过std::future来接收。我们来看下面的示例程序:

#include <iostream>
#include <future>
#include <chrono>
#include <thread>

int countdown(int from, int to)
{
	for (int i = from; i != to; --i)
	{
		std::cout << i << "\n";
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
	std::cout << "Lift off!\n";
	return from - to;
}

int main(void)
{
	std::packaged_task<int(int, int)> tsk(countdown); // set up pachaged_task
	std::future<int> ret = tsk.get_future();

	std::thread th(std::move(tsk), 10, 0);
	int value = ret.get();

	std::cout << "The countdown lasted for " << value << " seconds.\n";
	th.join();

	system("pause");
	return 0;
}

  上面的一个带有入参和返回值的函数,通过std::packaged_task类模板传入,通过std::future来接收异步返回值。std::packaged_task的共享状态生命周期将会持续到最后一个与之相关的对象被释放或者销毁。

std::async的使用

  关于异步线程函数std::async()能够比较便捷的获取线程函数的返回值,通过std::future对象来保存。关于std::async()函数的第一个参数下面的代码注释里面有做介绍,这里简短说下:

  • std::launch::async 参数代表异步函数std::async()立即执行;
  • std::launch::deferred 参数代表异步函数std::async()延迟执行,当调用wait()或者get()执行;
  • 默认情况下为std::launch::async | std::launch::deferred 依赖系统或者库函数第三方应用在某一个运行;
#include <iostream>
#include <chrono>
#include <thread>
#include <future>
#include <mutex>

bool is_prime(int x)
{
	std::cout << "Calculating, please wait...\n";
	for (int i = 2; i < x; ++i)
		if (0 == x % i)
			return false;
	return true;
}

int main(void)
{
	// call is_prime(313222313) asynchronously:
	// std::launch::async  立即执行
	// std::launch::deferred 延迟执行,直到调用get()函数
	// std::launch::async | std::launch::deferred 缺省默认方式,根据系统
	std::future<bool> fut = std::async(std::launch::deferred, is_prime, 313222313);

	std::this_thread::sleep_for(std::chrono::seconds(2));
	std::cout << "Checking whether 313222313 is prime.\n";

	bool ret = fut.get(); // waits for is_prime to return

	if (ret)
		std::cout << "It is prime.\n";
	else
		std::cout << "It is not prime.\n";

	system("pause");
	return 0;
}

  上述代码中如果你将std::async()异步函数第一个参数设置为std::launch::async表示立即执行,输出终端顺序可能有些不太一样。但是,如果你设置延迟执行std::launch::deferred,不论你在主线程是否设置延迟函数,它在调用get()函数时刻才开始执行。

小结

  好了,看到这里不知道你有没有一些疑惑?1. 为什么感觉std::promise能够传入数据,共享状态,std::packaged_task同样可以,并且似乎可以接受模板的方式入参,std::async也可以,为什么需要这么多的功能?其实主要是下面这样的应用:

  • std::promise主要是在异步线程中获取某个值,get_future()只能够调用一次;
  • std::packaged_task主要是获取异步线程中的返回值;
  • std::async主要是更加方便的使用,它不需要显示声明std::thread对象,更加方便;
参考

shared_future
async
promise
packaged_task

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值