c++11多线程编程(十):packaged_task介绍与实例

本节讨论c++11中std::packaged_task的特性与使用方法

std::packaged_task<>
std::packaged_task<>
是一个类模板,代表一个异步任务。封装了
1、可调用实体,即函数,lambda函数或函数对象
2、一个共享状态,通过关联的回调来存储返回的值或抛出的异常。

假设我们有一个现有的函数从数据库中提取数据并返回,

//从DB获取数据
std::string gtDataFromDB(std::string token){
	//Do some stuff to fetch the data
	std::string data = "Data fetched from DB by filter :: + token;
	return data;
}

现在我们想在一个单独的线程中执行该函数,但是我们如果在其他线程结束的情况下在主线程中获取结果或异常返回?
一种方法是更改函数声明并在函数中传递std::promise。在传递线程函数中的std::promise<>对象之前,将相关的std::future<>从中取出并保存在主线程中。现在,在线程函数返回其值之前,应该在传入的std::promise参数中设置它,所以它可以在主线程的相关std::future对象中使用。参考第八节

******************************************

但是,如果我们使用std::packaged_task<>,则可以避免创建std::promise和更改函数代码。


使用packaged_task<>创建异步任务
std::packaged_task<>
可以包装一个标准函数,使其适用于作为异步功能运行。
std::packaged_task<>在一个单独的线程中运行时,它会调用相关的回调函数,并将返回值或异常存储在其内部共享状态中。该值可以通过std :: future <>对象在其他线程或主函数中访问。

从上面提到的函数创建一个packaged_task<>,运行于独立的线程并从其future<>对象获取返回值。

创建std::packaged_task<> 对象

std::packaged_task<>对象是一个类模板,因此我们需要将模板参数传递给packaged_task<>,即可调用函数的类型。

//创建封装了回调函数的packaged_task<>
std::packaged_task task(getDataFromDB);

获取future对象

//从packaged_task<>获取相关future<>
std::future result = task.get_future();

传递packaged_task<>给线程

std::packaged_task<>可移动,但是不可复制,所以我们需要将它传递给线程

//传递packaged_task<>给线程以异步运行
std::thread th(std::move(task), "Arg");
由于packaged_task只可以移动,不可以复制,因此我们在将它移动到线程之前从它那里取出了 std::future<>对象。
线程将会执行这个任务,该任务在内部调用相关的可调用实体,例如我们的函数getDataFromDB()。
当这个函数给出返回值时,std::packaged_task<>将其设置为关联的共享状态,getDataFromDB()返回的结果或异常最终会在关联的未来对象中可用。

//获取packaged_task<>返回的结果,即getDataFromDB()返回的值。
std::string data = result.get();
get()函数将会阻塞调用线程,直到有可调用的实体返回,并且std::packaged_task<>将数据设置为其可共享的状态。


完整的例子如下:

#include 
#include 
#include 
#include 

//从DB获取数据
std::string getDataFromDB(std::string token) {
  //获取数据
  std::string data = "Data fetched from DB by Filter :: " + token;
  return data;
}

int main() {
  //创建封装回调函数的packaged_task<>
  std::packaged_task task(getDataFromDB);

  //从packaged_task<>获取相关的future<>
  std::future result = task.get_future();

  //将packaged_task传递给线程以异步运行
  std::thread th(std::move(task), "Arg");

  //join线程,阻塞直到线程完成时返回
  th.join();

  //获取packaged_task<>的结果,即getDataFromDB()的返回值
  std::string data = result.get();

  std::cout << data << std::endl;

  return 0;
}
输出:
Data fetched from DB by Filter :: Arg

我们可以在同一行,用lambda函数或函数对象创建一个packaged_task

使用lambda函数创建packaged_task

#include 
#include 
#include 
#include 

int main() {
  //创建封装了lambda函数的packaged_task<>
  std::packaged_task task([](std::string token) {
    std::string data = "Data From " + token;
    return data;
  });

  //从packaged_task<>获取相关的future<>
  std::future result = task.get_future();

  //将packaged_task传递给线程以异步运行
  std::thread th(std::move(task), "Arg");

  //join线程,阻塞直到线程完成时返回
  th.join();

  //获取packaged_task<>的结果,即getDataFromDB()的返回值
  std::string data = result.get();

  std::cout << data << std::endl;

  return 0;
}
输出:
Data fetched from DB by Filter :: Arg


使用函数对象创建packaged_task
#include 
#include 
#include 
#include 

//从DB获取数据的函数对象
struct DBDataFetcher {
  std::string operator ()(std::string token) {
    std::string data = "Data From " + token;
    return data;
  }
};

int main() {
  //使用函数对象创建packaged_task
  std::packaged_task task(std::move(DBDataFetcher()));

  //从packaged_task<>获取相关的future<>
  std::future result = task.get_future();

  //将packaged_task传递给线程以异步运行
  std::thread th(std::move(task), "Arg");

  //join线程,阻塞直到线程完成时返回
  th.join();

  //获取packaged_task<>的结果,即getDataFromDB()的返回值
  std::string data = result.get();

  std::cout << data << std::endl;

  return 0;
}
输出:
Data fetched from DB by Filter :: Arg

你可能感兴趣的:(c/c++,c++11多线程编程)