C++异步操作,std::future,std::aysncm,std::promise,std::packaged_task,std::bind

  • std::futrue:异步指向某个任务,然后通过future特性去获取函数的返回结果
  • std::async:异步运行某个任务函数
  • std::packaged_task:将任务和future绑定在一起的模板,是一种对任务的封装
  • std::promise

一. std::async和std::future

1. std::future

  • std::future期待一个返回,从一个异步调用的角度来说,future更像是执行函数的返回值,C++标准库使用std::future为一次性事件建模,如果一个事件需要等待特定的一次性事件,那么这线程可以获取一个future对象来代表这个事件。
  • 异步调用往往不知道何时返回,但是如果异步调用的过程需要同步,或者说后一个异步调用需要前一个异步调用的结果。这个时候就要用到std::future。
  • 线程可以周期性的在这个future上停留一段时间,检查future是否已经ready,如果没有,该线程可以先去做另一个任务,一旦future就绪,该future就无法复位(无法再次使用这个future等待这个事件),所以future代表的是一次性事件。

2 std::future的类型

  • 在< future >库的头文件中声明了两种future,唯一future(std::future)和共享future(std::shared_future)
  • 这两个是参照std::unique_ptr和std::shared_ptr设计的,前者的实例是仅有的一个指向其关联事件的实例,而后者可以有多个实例指向同一个关联事件,当事件就绪时,所有指向同一事件的std::shared_future实例都会变成就绪

3 std::future和std::async的使用

  • std::future是一个模板,例如std::future< int >,模板参数就是期待返回的类型,虽然future被用于线程间通信,但其本身并不提供同步访问,因此我们必须通过互斥元或其他同步机制来保护访问
  • future使用的时机是当你不需要立刻得到一个执行结果的时候,你可以开启一个线程去帮你做一项任务,并期待这个任务的返回,但是std::thread并没有这样的机制,这就需要用到std::async和std::future(都在< future >头文件中声明)
  • std::async用于创建异步任务,实际上就是创建一个线程执行相应任务。
  • std::async返回一个std::future对象,而不是给你一个确定的值(所以当你不需要立刻使用此值的时候才用到这个机制);在你需要使用这个值的时候,对std::future使用get()方法,线程就会阻塞直到future就绪,然后返回该值,例如:
int sum( int a, int b )
{
	std::this_thread::sleep_for( std::chrono::second(5) );
	// a series of complex operations...(non-main thread)
	return a + b;
}

int main()
{
	std::future<int> result = std::async(sum, 10, 20);
	
	// do other things...(main thread)
	
	std::cout << result.get() << std::endl;
	return 0;
}

二. std::packaged_task

std::packaged_task的作用是提供一个不同线程之间的数据同步机制,它可以存储一个函数操作,并将其返回值传递给对应的future,而这个future在另一个线程中也可以安全的访问这个值。

  • std::packaged_task是将任务和feature绑定在一起的模板,是一种对任务的封装
  • 可以通过std::packaged_task对象获取任务相关联的feature,调用get_future()方法可以获得std::packaged_task对象绑定的返回值类型的future
  • std::packaged_task的模板参数是函数签名,例如:int add(int a, int b)的函数签名就是int(int int)
int add(int a, int b, int c)
{
	std::cout << "call add" << std::endl;
	return a+b+c;
}

int main()
{
	std::packaged_task<int(int, int, int)> task(add); // 1.封装任务,还没有执行add函数
	std::this_thread::sleep_for( std::chrono::second(5) );
	
	// do other things...
	
	std::future<int> result = task.get_future(); // 2.这里只是获取future,同样没有执行
	task(1, 1, 2); // 3.必须要让任务执行,否则在get()获取执行结果的时候会一直阻塞
	std::cout << "result:" << result.get() << std::endl;	
	return 0;
}

三. std::promise

std::promise提供了不同线程之间的数据同步方式,可存储一个某种类型的值,并将其传递给对应的future,即使这个future不在同一个线程中也能访问到这个值。

void print1( std::promise<std::string>& p )
{
	std::cout << "print1 sleep for 3 seconds..." << std::endl;
	std::this_thread::sleep_for( std::chrono::second(3) );
	p.set_value("hello world"); // 返回future结果
}

int main()
{
	std::promise<std::string> promise;
	std::future<std::string> result = promist.get_future(); // 获取promise对应的future
	std::thread t( print1, std::ref(promise) ) // 线程设置 传引用std::ref
	
	// do other things...
	
	std::cout << "wait for result..." << std::endl;
	std::cout << "result:" << result.get() << std::endl; // 在主线程等待 promise的返回
	return 0;
}

四. std::bind

函数的绑定

void fun_1(int a, int b, int c)
{
	std::cout << a << " " << b << "" << c << std::endl;
}

int main()
{
	auto f1 = std::bind( fun_1, 1, 2,3 ); // 绑定函数fun_1的第1、2、3个参数为:1、2、3
	f1();
	auto f11 = std::bind( fun_1, 10, 20, 30 ); // 绑定函数fun_1的第1、2、3个参数为:10、20、30
	f11();

	// std::placeholder:c++11中的占位符
	auto f2 = std::bind( fun_1, std::placeholder::_1, std::placeholder::_2, 3 ); 
	// 表示绑定函数fun_1的第3个参数为3,而func_1的第一、第二个参数分别由调用f2的第一、第二个参数指定
	f2(1, 2); // print:1 2 3
	f2(10, 21, 30); // 传入30也没用,print:10 20 3

	return 0;
}

你可能感兴趣的:(c++,开发语言,算法)