C++11中std::packaged_task的使用详解

这里我将为你详细讲解“C++11中std::packaged_task的使用详解”的完整攻略。

1. 什么是std::packaged_task

std::packaged_task 是一个封装了一个可调用对象(函数,函数指针,lambda表达式等)的类模板,其可以异步地执行该可调用对象,并在需要时获取该对象的结果。

std::packaged_task 可以通过 std::future 来获取调用结果,可以使用 future.get() 来获取结果,或者等待 future 函数执行完成再获取结果。

2. std::packaged_task的使用方法

2.1 使用示例1:使用 std::packaged_task 异步执行函数并获取结果

#include <iostream>
#include <thread>
#include <future>
#include <functional>

int foo(int x, int y) {
    std::cout << "foo excuted" << std::endl;
    return x + y;
}

int main() {
    std::packaged_task<int(int, int)> task(&foo);
    std::future<int> fut = task.get_future();

    std::thread(std::move(task), 10, 20).detach();
    std::cout << "Waiting...\n";
    fut.wait(); // 等待 foo 执行完成
    std::cout << "Result: " << fut.get() << std::endl; // 获取结果

    return 0;
}

该示例中,我们使用 std::packaged_task 封装了一个 foo 函数,并创建了一个 std::future 实例来获取函数的执行结果。接着我们创建了一个 std::thread 的实例来异步执行 foo 函数,最后在主线程中调用 fut.wait() 等待函数执行的完成,并通过 fut.get() 获取函数的执行结果。示例中的输出结果如下:

Waiting...
foo excuted
Result: 30

2.2 使用示例2:使用 std::packaged_task 实现简单的线程池

在这个示例中,我们将使用一个 std::queue 来存储 std::packaged_task 实例,当有任务加入队列时,我们将创建一个新的线程来执行该任务,并从队列中移除该任务。该示例中的线程池实现采用了简单的线程安全措施,避免了多个线程同时操作队列产生竞争条件。

#include <iostream>
#include <thread>
#include <future>
#include <functional>
#include <queue>
#include <mutex>
#include <condition_variable>

int foo(int x, int y) {
    std::cout << "foo excuted" << std::endl;
    return x + y;
}

class ThreadPool {
public:
    ThreadPool(size_t num_threads) {
        for (int i = 0; i < num_threads; ++i) {
            workers.emplace_back([this] {
                for (;;) {
                    std::packaged_task<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock, [this] {
                            return this->stop || !this->tasks.empty();
                        });
                        if (this->stop && this->tasks.empty()) {
                            return;
                        }
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    template<typename Func, typename... Args>
    auto enqueue(Func&& func, Args&&... args) 
    -> std::future<typename std::result_of<Func(Args...)>::type> {
        using return_type = typename std::result_of<Func(Args...)>::type;
        std::packaged_task<return_type()> task(
            std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
        );
        std::future<return_type> fut = task.get_future();
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            // 禁止在停止线程池之后加入队列
            if (stop) {
                throw std::runtime_error("enqueue on stopped ThreadPool");
            }
            tasks.emplace(std::move(task));
        }
        condition.notify_one();
        return fut;
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread& worker : workers) {
            worker.join();
        }
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::packaged_task<void()>> tasks;

    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop = false;
};

int main() {
    ThreadPool pool(4);

    std::vector<std::future<int>> results;

    for (int i = 0; i < 8; ++i) {
        results.emplace_back(pool.enqueue(foo, i, i + 1));
    }
    for (auto&& result : results) {
        std::cout << result.get() << ' ';
    }
    std::cout << std::endl;

    return 0;
}

在该示例中,我们定义了一个名为 ThreadPool 的类来表示线程池。其中,构造函数接受一个整数表示线程池的大小,我们用 std::vector<std::thread> 来存储线程池中的所有线程。每个线程不断地从任务队列中取得任务,然后执行任务。

ThreadPool 中,我们定义了一个模板成员函数 enqueue 来将任务加入队列。该函数接受一个任意可调用的参数作为要执行的任务,函数将返回一个 std::future 对象,以便调用方获取任务的执行结果。

在主函数中,我们创建了一个 ThreadPool 实例,然后向线程池中加入了8个任务,每个任务都是 foo 函数的调用,并将任务的返回值存储在一个 std::future 对象中。最后,我们遍历所有结果并输出。

该示例中的输出结果如下:

foo excuted
foo excuted
foo excuted
foo excuted
foo excuted
foo excuted
foo excuted
foo excuted
1 3 5 7 9 11 13 15
阅读剩余 70%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++11中std::packaged_task的使用详解 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • C语言三种函数调用约定_cdecl与_stdcall及_fastcall详细讲解

    C语言三种函数调用约定_cdecl与_stdcall及_fastcall详细讲解 在C语言中,函数调用约定(Function Call Convention)是指编程语言如何在调用函数时传参、返回值等一系列约定。C语言中常用的函数调用约定有三种:cdecl、stdcall和fastcall。本文将对这三种函数调用约定进行详细讲解。 cdecl调用约定 cde…

    C 2023年5月22日
    00
  • 一篇文章彻底弄懂C++虚函数的实现机制

    一篇文章彻底弄懂C++虚函数的实现机制 介绍 C++的虚函数是实现多态的重要途径,本文将深入浅出地介绍C++虚函数的实现机制,希望能给大家带来一些帮助。 基本概念 静态绑定和动态绑定 在C++中,有两种绑定方式,即静态绑定(也称为静态链接)和动态绑定(也称为动态链接)。 静态绑定是指在编译期间确定函数的调用地址。这种方式的优点是执行速度快,缺点是不支持多态。…

    C 2023年5月23日
    00
  • 如何用C语言编写PHP扩展的详解

    如何用C语言编写PHP扩展的详解 一个PHP扩展是由C语言写的动态链接库,它可以用来扩展PHP的功能,提高PHP代码的性能。编写PHP扩展可以让我们在PHP代码中使用C语言提供的高效、强大的功能,并且可以与PHP代码无缝集成。 编写PHP扩展的详细流程如下: 准备环境 在开始编写PHP扩展之前,需要准备好下面的环境: PHP源代码(需要与扩展编写的PHP版本…

    C 2023年5月23日
    00
  • C++中的数据内存分布原理

    C++中的数据内存分布原理 在理解C++程序的底层运行原理时,必须深入理解数据内存分布的基本原理。 1. 内存地址和指针 内存地址是指内存单元在内存中所对应的位置,通常用十六进制数字表示。内存单元是计算机分配给程序使用的最小单位,通常是8个比特(1字节)。 指针是一个用来存储内存地址的变量,C++中的指针可以用来进行动态内存分配,或者通过指针访问数组和其他数…

    C 2023年5月22日
    00
  • C#格式化json字符串的方法分析

    下面就是详细的讲解: C# 格式化 JSON 字符串的方法分析 JSON 是一种轻量级的数据交换格式,常用于前后端数据传输。在开发中,我们通常需要将对象转换为 JSON 格式的字符串,或者将 JSON 格式的字符串转换为对象。本文会着重讲解 C# 中如何格式化 JSON 字符串。 使用JsonConvert.SerializeObject() 在 C# 中使…

    C 2023年5月23日
    00
  • C++语言编写写日志类

    下面就是“C++语言编写写日志类”的完整攻略: 1. 确定日志类的功能和需求 在编写日志类之前,需要考虑其功能和需求,如何记录日志、日志信息的格式、日志级别、日志存储路径等。根据实际需求设计日志类的各项功能,比如: 记录日志信息 支持不同的日志级别 支持自定义日志存储路径和文件名 自动切分日志文件 支持日志的异步输出,避免阻塞主线程等待写日志操作 在这里,我…

    C 2023年5月22日
    00
  • C++构造析构赋值运算函数应用详解

    C++构造析构赋值运算函数应用详解 什么是构造函数、析构函数和赋值运算函数 在C++语言中,构造函数、析构函数和赋值运算函数都是面向对象编程中的重要概念。 构造函数:用于对象的初始化工作,它在对象被创建时自动调用,一般不需要手动调用。 析构函数:用于对象的销毁工作,它在对象被删除时自动调用,同样也不需要手动调用。 赋值运算函数:用于对象的赋值操作,即将一个对…

    C 2023年5月23日
    00
  • C++连接并使用MySQL数据库

    一、C++连接MySQL数据库简介C++是一门非常流行的编程语言,除了可以进行基本的编程外,它还可以连接多种数据库进行数据操作,其中之一就是MySQL数据库。在本篇文章中,我们将讲解如何使用C++连接并操作MySQL数据库,并提供用C++语言的示例代码。 二、安装MySQL C++ Connector在使用C++连接MySQL数据库之前,需要先安装MySQL…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部