C++11——多线程编程10
翻译来自:https://thispointer.com/c11-multithreading-part-10-packaged_task-example-and-tutorial/
这一节我们将讨论 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和更改函数代码。
创建std::packaged_task<> 对象
std::packaged_task<>对象是一个类模板,因此我们需要将模板参数传递给packaged_task<>,即可调用函数的类型。
//创建封装了回调函数的packaged_task<> std::packaged_task<std::string(std::string)> task(getDataFromDB);
获取future对象
//从packaged_task<>获取相关future<> std::future<std::string> 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 <iostream> #include <thread> #include <future> #include <string> //从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<std::string(std::string)> task(getDataFromDB); //从packaged_task<>获取相关的future<> std::future<std::string> 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
#include <iostream> #include <thread> #include <future> #include <string> int main() { //创建封装了lambda函数的packaged_task<> std::packaged_task<std::string(std::string)> task([](std::string token) { std::string data = "Data From " + token; return data; }); //从packaged_task<>获取相关的future<> std::future<std::string> 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 <iostream> #include <thread> #include <future> #include <string> //从DB获取数据的函数对象 struct DBDataFetcher { std::string operator ()(std::string token) { std::string data = "Data From " + token; return data; } }; int main() { //使用函数对象创建packaged_task std::packaged_task<std::string(std::string)> task(std::move(DBDataFetcher())); //从packaged_task<>获取相关的future<> std::future<std::string> 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