[编程基础] C++多线程入门10-packaged_task示例

原始C++标准仅支持单线程编程。新的C++标准(称为C++11或C++0x)于2011年发布。在C++11中,引入了新的线程库。因此运行本文程序需要C++至少符合C++11标准。

10 packaged_task<>示例

在此示例中,我们将讨论c++11中std::packaged_task功能及其用法。std::packaged_task<>是一个类模板,代表一个异步任务。它封装了:

  1. 可调用实体,即函数,lambda函数或函数对象。
  2. 一种共享状态,用于存储由关联的回调返回或引发的异常的值。

需要std::packaged_task<>的情况
假设我们有一个现有函数,该函数从数据库中获取数据并返回

// Fetch some data from DB
std::string getDataFromDB(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 <>和更改功能代码。

10.1 结合使用packaged_task <>和函数来创建异步任务

std::packaged_task <>可以包装普通函数,并使其可作为异步函数运行。在单独的线程中调用std:: packaged_task <>时,它将调用关联的回调并将返回值/异常存储在其内部共享状态中。可以通过std:: future <>对象在其他线程或主函数中访问此值。让我们从上述函数创建一个std::packaged_task <>,在单独的线程中执行,并从其future <>对象获取结果。
创建std::packaged_task <>对象
std::package_task <>是类模板,因此我们需要将模板参数传递给packaged_task <>,即可调用函数的类型

// Create a packaged_task<> that encapsulated the callback i.e. a function
std::packaged_task<std::string (std::string)> task(getDataFromDB);

从中获取future对象

// Fetch the associated future<> from packaged_task<>
std::future<std::string> result = task.get_future();

将packaged_task <>传递给线程
std::packaged_task <>是可移动的,但不可复制,因此我们需要将其移动到线程,即

// Pass the packaged_task to thread to run asynchronously
std::thread th(std::move(task), "Arg");

由于packaged_task仅可移动且不可复制,因此我们在将其移至线程之前从其获取了std::future <>对象。线程将执行此任务,该任务在内部调用关联的可调用实体,即我们的函数getDataFromDB()。
现在,当此函数返回值时,std::packaged_task <>会将其设置为关联的共享状态,并且getDataFromDB()返回的结果或异常最终将在关联的future对象中可用。

在主函数中,从future <>对象获取结果,即

// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
std::string data =  result.get();

get()函数将阻塞调用线程,直到可调用实体返回并且std::packaged_task <>将数据设置为可共享状态为止。完整的示例代码如下:

#include <iostream>
#include <thread>
#include <future>
#include <string>
// Fetch some data from DB
std::string getDataFromDB(std::string token)
{
    // Do some stuff to fetch the data
    std::string data = "Data fetched from DB by Filter::" + token;
    return data;
}
int main()
{
    // Create a packaged_task<> that encapsulated the callback i.e. a function
    // 创建封装回调函数的packaged_task<>
    std::packaged_task<std::string(std::string)> task(getDataFromDB);
    // Fetch the associated future<> from packaged_task<>
    // 从packaged_task<>中获取关联的future<>对象
    std::future<std::string> result = task.get_future();
    // Pass the packaged_task to thread to run asynchronously
    // 将packaged_task传递给线程以异步运行
    std::thread th(std::move(task), "Arg");
    // Join the thread. Its blocking and returns when thread is finished.
    // 加入线程,完成后返回
    th.join();
    // Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
    // 获取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 <iostream>
#include <thread>
#include <future>
#include <string>
int main()
{
    // Create a packaged_task<> that encapsulated a lambda function
    std::packaged_task<std::string(std::string)> task([](std::string token) {
        // Do some stuff to fetch the data
        std::string data = "Data From " + token;
        return data;
    });
    // Fetch the associated future<> from packaged_task<>
    std::future<std::string> result = task.get_future();
    // Pass the packaged_task to thread to run asynchronously
    std::thread th(std::move(task), "Arg");
    // Join the thread. Its blocking and returns when thread is finished.
    th.join();
    // Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
    std::string data = result.get();
    std::cout << data << std::endl;
    return 0;
}

输出为:

Data From Arg

使用函数对象创建packaged_task

#include <iostream>
#include <thread>
#include <future>
#include <string>
/*
* Function Object to Fetch Data from DB
*/
struct DBDataFetcher
{
    std::string operator()(std::string token)
    {
        // Do some stuff to fetch the data
        std::string data = "Data From " + token;
        return data;
    }
};
int main()
{
    // Create a packaged_task<> that encapsulated a lambda function
    std::packaged_task<std::string(std::string)> task(std::move(DBDataFetcher()));
    // Fetch the associated future<> from packaged_task<>
    std::future<std::string> result = task.get_future();
    // Pass the packaged_task to thread to run asynchronously
    std::thread th(std::move(task), "Arg");
    // Join the thread. Its blocking and returns when thread is finished.
    th.join();
    // Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
    std::string data = result.get();
    std::cout << data << std::endl;
    return 0;
}

输出为:

Data From Arg

10.2 参考

https://thispointer.com/c11-multithreading-part-10-packaged_task-example-and-tutorial/

原文链接: https://www.cnblogs.com/luohenyueji/p/16970262.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    [编程基础] C++多线程入门10-packaged_task示例

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/351530

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 上午6:54
下一篇 2023年3月2日 上午6:54

相关推荐