2_6_9 async、future、packaged_task、promise、shared_future、atomic

6_9_async_future_packaged_task_pro.cpp

#include "hjcommon.hpp"
#include <future>

using namespace std;
HJ_NS_USING

static int aFunc(int num)
{
	cout << "aFunc start. : " << num << ", threadId=" << this_thread::get_id() << endl;
	sleep(1000*1000);
	cout << "aFunc end." << endl;
	return 5;
}
static int bFunc(int num)
{
	cout << "bFunc. : " << num << ", threadId=" << this_thread::get_id() << endl;
	return 2;
}
class A
{
public:
	int operator()(int num)
	{
		cout << "operator(). : " << num << ", threadId=" << this_thread::get_id() << endl;
		return 3;
	}
};
static void cFunc(std::promise<int> &pro, int calc)
{
	calc *= 10;
	cout << "cFunc. : " << calc << ", threadId=" << this_thread::get_id() << endl;
	sleep(1000*1000);
	int ret = calc;
	pro.set_value(ret); // 不能直接保存 calc 值,编译报错..
}
static int dFunc()
{
	sleep(1s);
	cout << "dFunc. threadId=" << this_thread::get_id() << endl;
	return 3;
}

int main_2_6_9(int argc, char *argv[])
{
	// std::async函数模板 std::future类模板 创建后台任务并返回值
	cout << "主线程:" << this_thread::get_id() << endl;
	std::future<int> fut = std::async(std::launch::deferred, aFunc, 1); // 创建子线程执行aFunc函数,传参的行为与thread传参一样
	// get() 与 wait() 只能调一个,都调用程序会抛宕,相同的get或wait函数也只能调用一次。 若两个函数都不调用,行为有点类似detach,但是程序依然会等待线程执行完毕
	int ret = fut.get(); // 会阻塞,直到线程执行完返回
//	fut.wait(); // wait()只会阻塞直到线程执行完,拿不到线程函数返回值
	// std::launch::deferred(枚举) : 表示线程函数会延迟到程序主动调用get()或wait()函数后才会执行,
		// 此时线程函数是在主线程中执行的,也就是async()没有去创建子线程了,此时若不get()或wait(),那么线程函数根本不会执行。 std::launch::async(另一个值)

	// std::packaged_task类模板 : 将可调用对象包装起来,供后续调用,模板参数为可调用对象类型
//	std::packaged_task<int(int)> pt1(bFunc); // 包装 bFunc 函数
//	std::packaged_task<decltype(bFunc)> pt1(bFunc); // 效果同上,<> 里可以用decltype(bFunc)
	A a;
	std::packaged_task<int(int)> pt1(a); // 效果同上,但是 <> 里不能用decltype(a)。。
	thread t1(std::ref(pt1), 2); // 参数 2 表示线程函数形参
	t1.join();
	future<int> fut2 = pt1.get_future(); // 将 packaged_task 与 future 关联,可以获取线程函数返回值
	cout << "packaged_task包装函数 : " << fut2.get() << endl; // 由于上面thread.join() 过了,所着这里futire.get()不会再阻塞而是直接拿到线程函数返回值
	// packaged_task 包装 lambda
	auto lambda = [](int num) {
		cout << "lambda. : " << num << ", threadId=" << this_thread::get_id() << endl;
		return 4;
	};
	packaged_task<int(int)> pt2(lambda); // <>里不能用decltype(lambda)。。
	thread t2(std::ref(pt2), 4);
	t2.join();
	cout << "packaged_task包装lambda : " << pt2.get_future().get() << endl;
	// packaged_task 对象也是可调用对象,直接调用等于调用其包装的可调用对象,若packaged_task不管以何种方式调用过,就不能再次调用了,会抛宕.
	packaged_task<int(int)> pt3(lambda);
	pt3(1121);
	cout << "packaged_task直接调用包装的可调用对象 : " << pt3.get_future().get() << endl;
	// packaged_task 也可以存入到容器中,注意每次取出与存入都要用移动语义
	vector<packaged_task<int(int)>> vec;
	vec.push_back(packaged_task<int(int)>(lambda));
	auto iter = vec.begin();
	packaged_task<int(int)> pt4 = std::move(*iter);
	vec.erase(iter); // 移除
	pt4(414);
	cout << "packaged_task容器移动语义 : " << pt4.get_future().get() << endl; // 这里若没有调用可调用对象(pt4(414);),同样也会被阻塞,直到调用完该可调用对象

	// std::promise 类模板 : 在一个线程中保存某个值,在其他线程中可以取出来,promise.set_value(val) promise.get_future().get()
	std::promise<int> pro;
	thread t3(cFunc, std::ref(pro), 3); // 需要是同一个promise,不能拷贝,所以用了std::ref()
	t3.join();
	future<int> fut3 = pro.get_future(); // primise 与 future 关联
	// 当promise.get_future().get()时,若其他地方还没有调用promise.set_value()那么会阻塞当前线程,直到promise.set_value()..
	cout << "promise获取线程中保存的值 : " << fut3.get() << endl;

	// 测试 packaged_task.get_future().get() 与 thread.detach() 是否会阻塞
	packaged_task<int(void)> pt(dFunc);
	Thread t(std::ref(pt));
	t.detach();
	cout << "dFunc return=" << pt.get_future().get() << endl; // 只要pt.get_future().get()没有拿到返回值,线程就算是detach,也会阻塞等待返回值

	cout << "end." << endl;
	return 0;
}

6_10_shared_future_atomic.cpp

#include "hjcommon.hpp"
#include <future>
#include <mutex>

using namespace std;
HJ_NS_USING

static int aFunc()
{
	cout << "aFunc start. threadId=" << this_thread::get_id() << endl;
	sleep(2000*1000);
	cout << "aFunc end." << endl;
	return 1;
}
static int bFunc()
{
	cout << "bFunc threadId=" << this_thread::get_id() << endl;
	return 2;
}
// 原子操作,int,默认值0,需要包含 #include <future> ,使用 std::atomic<int> g_num = 0; 编译会报隐式转换错误。。使用 std::atomic<int> g_num(0); eclipse标红。。
static std::atomic<int> g_num;
static void cFunc()
{
	for (int i=0; i<10000000; ++i)
		g_num++;
}

int main_2_6_10(int argc, char *argv[])
{
	cout << "主线程:" << this_thread::get_id() << endl;
	std::future<int> fut1 = std::async(std::launch::deferred, aFunc);
	std::future_status status = fut1.wait_for(std::chrono::seconds(3)); // 阻塞参数时长,future_status枚举, ready, timeout, deferred
//	std::future_status status = fut1.wait_until(); // 与 wait_for 类似
	switch (status)
	{
	case std::future_status::timeout: // 超时
		cout << "超时" << endl;
		break;
	case std::future_status::ready: // 成功返回
		cout << "成功返回 : " << fut1.get() << endl;
		break;
	case std::future_status::deferred: // std::async()第一个参数传 std::launch::deferred 时,wait_for返回此状态,而且wait_for()函数不会阻塞
		cout << "std::launch::deferred" << endl;
		break;
	}

	// std::shared_future : std::future.get()函数是移动语义,所以只能get一次,使用shared_future实现可以get多次(复制)
	packaged_task<int(void)> pt2(bFunc);
	thread t2(ref(pt2));
	t2.join();
	future<int> fut2 = pt2.get_future();
	cout << "future.valid()=" << fut2.valid() << endl; // future.valid()=1, future.valid()返回future是否还能get()值
	std::shared_future<int> sf2(fut2.share()); // fut2.share() 等同于 std::move(fut2)
	cout << "future.valid()=" << fut2.valid() << endl; // future.valid()=0
	int ret = sf2.get();
	ret = sf2.get();

	// std::atomic类模板 原子操作 : 指不会被打断的程序执行片段, 原子操作效率上比mutex要高, 但原子操作只能针对一个变量,而mutex可以是代码段
	g_num.store(0); // 往 atomic 中写值,原子操作
	thread t3(cFunc); // 两个线程同时大循环写同一个变量
	thread t4(cFunc);
	t3.join();
	t4.join();
	// 已经join()了,所以理论上每次执行结果值应该都是20000000, 但是由于原子操作的问题,所以每次执行结果不一定都是20000000
	// 由于cpu切换造成线程中断,如果中断的时候一个线程中刚好是要加但未加完,然后cpu去另一个线程执行加法,那么此时第一个线程中的加法就不会成功,所以最终的结果可能会比20000000小
	// 虽然此问题可以用mutex加锁解决,但是执行效率会大大降低.
	cout << "原子操作g_num=" << g_num << endl;

	cout << "end." << endl;
	return 0;
}

6_11_atomic_async.cpp

#include "hjcommon.hpp"
#include <future>

using namespace std;
HJ_NS_USING

static std::atomic<int> g_num;
static void func()
{
	for (int i=0; i<10000000; ++i)
	{
		g_num++; // atomic对于 ++ -- += -= &= |= 等运算符结果是正确的,但是 g_num = g_num+1; 这种方式是不行。。
		g_num += 1;
//		g_num = g_num+1;
	}
}
static void bFunc()
{
	cout << "bFunc. threadId=" << this_thread::get_id() << endl;
}

int main_2_6_11(int argc, char *argv[])
{
	// atomic对于 ++ -- += -= &= |= 等运算符结果是正确的,但是 g_num = g_num+1; 这种方式是不行。。
	g_num.store(10); // 往 atomic 中写值,原子操作
	atomic<int> ato2;
	ato2.store(g_num.load()); // load() 函数,读取 atomic 的值,原子操作的

	thread t1(func);
	thread t2(func);
	t1.join();
	t2.join();
	cout << "原子操作g_num=" << g_num << endl;

	// async
	cout << "主线程:" << this_thread::get_id() << endl;
//	future<void> fut1 = async(std::launch::async, bFunc); // std::launch::async表示强制创建新线程,线程函数会在此代码后就开始执行,不用等future.get()
	// std::launch::async | std::launch::deferred 的行为可能是async的行为,也可能是deferred的行为。。 看系统调度,可用future.wait_for()看是哪种行为
//	future<void> fut1 = async(std::launch::async | std::launch::deferred, bFunc);
	future<void> fut1 = async(bFunc); // 当不指定launch参数时,默认值为 std::launch::async | std::launch::deferred
	future_status status = fut1.wait_for(0s); // 0s 表示0秒, c++11中支持此种运算符重载(operator""s等)  0ms:0毫秒 0us:0微秒 0ns:0纳秒 0h:0小时 0min:0分钟
	cout << "status==future_status::deferred ? " << (status==future_status::deferred) << endl;
	fut1.get();

	cout << "end." << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值