C++实现一个简单的线程池的示例代码

下面是实现简单线程池的代码攻略。

什么是线程池?

线程池是一种用于管理多线程执行的机制,允许在需要时提供可分配的工作线程集中的线程。使用线程池的好处是可以减少线程的创建和销毁次数,避免线程频繁创建和销毁所带来的开销,也可以避免同时开启大量的线程造成系统资源的过度占用。在实际生产环境中,线程池通常具有限制线程数量、任务队列、线程管理等功能。

C++实现线程池的示例代码

下面我们以C++为例,使用C++11中的线程库,实现一个简单的线程池。主要的类和函数有:

  • std::thread:C++11中的线程类,用于创建新线程。

  • std::mutex:C++11中的互斥量类,用于线程间的互斥访问。

  • std::condition_variable:C++11中的条件变量类,用于线程间的同步。

  • std::function:C++11中的函数类,用于存储任意可调用对象,包括函数指针、成员函数指针、lambda表达式等。

  • std::queue:C++中的队列类,用于存储任务列表。

下面是一个简单的线程池实现代码。该线程池的主要功能包括添加任务、执行任务和关闭线程池。

#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <queue>
#include <vector>

class ThreadPool {
public:
    ThreadPool(int num_threads) : is_running_(true) {
        for (int i = 0; i < num_threads; ++i) {
            threads_.emplace_back(std::bind(&ThreadPool::ThreadFunc, this));
        }
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex_);
            is_running_ = false;
        }
        cv_.notify_all();

        for (auto& thread : threads_) {
            thread.join();
        }
    }

    void AddTask(std::function<void()> task_func) {
        std::unique_lock<std::mutex> lock(queue_mutex_);
        tasks_.push(std::move(task_func));
        cv_.notify_one();
    }

private:
    void ThreadFunc() {
        while (is_running_) {
            std::unique_lock<std::mutex> lock(queue_mutex_);
            cv_.wait(lock, [this] { return !tasks_.empty() || !is_running_; });

            if (!is_running_) {
                break;
            }

            auto task = std::move(tasks_.front());
            tasks_.pop();

            lock.unlock();

            task();
        }
    }

    bool is_running_;
    std::mutex queue_mutex_;
    std::condition_variable cv_;
    std::queue<std::function<void()>> tasks_;
    std::vector<std::thread> threads_;
};

上述代码中,创建线程池时,需要指定线程池的线程数量,然后创建相应数量的工作线程。每个工作线程都执行ThreadFunc()函数中的操作。ThreadFunc()函数中不断地查询任务队列,如果队列为空就等待,直到队列中有新的任务进来或者线程池已经被终止。

当添加新任务时,需要将任务函数存储到任务队列中,并唤醒一个等待的线程执行任务。

关闭线程池时,需要将每个工作线程终止,并删除队列中尚未执行的任务。

示例代码说明

这里给出两个简单的示例代码说明:

  • 示例1:使用线程池求和
#include <iostream>
#include "ThreadPool.h"

int Sum(int a, int b) {
    std::cout << "Thread " << std::this_thread::get_id() << ": Sum a and b\n";
    return a + b;
}

void main() {
    ThreadPool thread_pool(4);

    std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    auto func = [](int n) { return n * n; };

    int sum = 0;
    for (auto n : numbers) {
        thread_pool.AddTask([&sum, n]() { sum += n; });
    }

    auto result_future = thread_pool.AddTask(std::bind(Sum, 100, 200));
    std::cout << "Thread " << std::this_thread::get_id() << ": Submi task for Sum(100, 200)\n";

    for (auto n : numbers) {
        thread_pool.AddTask(std::bind([&sum](int n) { sum += n; }, func(n)));
    }

    std::cout << "Thread " << std::this_thread::get_id() << ": Waiting for result\n";

    std::cout << "Result: " << sum + result_future.get() << std::endl;
}

上述代码中,首先创建了一个线程池thread_pool,然后向线程池中添加了多个任务,如对数列中的每个数求平方和,以及调用Sum函数计算100和200的和。其中,Sum函数是一个简单的计算两个参数和的函数。

可以看到,添加任务时,可以使用lambda表达式、std::functionstd::bind等方式来封装任务函数。使用线程池可以减少代码中的线程创建及管理开销。

  • 示例2:使用线程池处理图像
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include "ThreadPool.h"

void ProcessImage(cv::Mat& image) {
    int channels = image.channels();
    int n_rows = image.rows;
    int n_cols = image.cols * channels;

    for (int i = 0; i < n_rows; ++i) {
        uchar* data = image.ptr<uchar>(i);
        for (int j = 0; j < n_cols; ++j) {
            data[j] = 255 - data[j];
        }
    }
}

void main() {
    cv::Mat image = cv::imread("sample.jpg");
    if (image.empty()) {
        std::cout << "Cannot read image file" << std::endl;
        return;
    }

    ThreadPool thread_pool(4);

    std::vector<cv::Mat> image_parts;
    int num_parts = 4;
    int part_rows = image.rows / num_parts;

    for (int i = 0; i < num_parts; ++i) {
        cv::Mat image_part(image, cv::Rect(0, i * part_rows, image.cols, part_rows));
        image_parts.push_back(image_part);
    }

    std::vector<std::future<void>> results;
    for (auto& part : image_parts) {
        results.push_back(thread_pool.AddTask(std::bind(ProcessImage, std::ref(part))));
    }

    for (auto& result : results) {
        result.get();
    }

    cv::imwrite("result.jpg", image);
}

这是一个简单的图像处理示例,在使用原始方式处理图像时,需要在循环中遍历图像的每个像素进行处理,这样的操作需要较长的时间。而在使用线程池方式处理图像时,可以将图像分成几个部分,让每个线程单独处理一部分图像,然后等处理完成后合并成完整的图像。这样,可以大大缩短图像处理时间。

在示例代码中,首先读取一张图片sample.jpg,然后将图片分成4部分,并向线程池中添加图像处理任务。任务处理完成后,等待各个任务执行完毕,将处理后的图像保存为result.jpg。可以看到,使用线程池处理图像需要创建很少的线程,且线程资源得到高效的利用。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++实现一个简单的线程池的示例代码 - Python技术站

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

相关文章

  • C++函数重载的定义与原因详解

    C++函数重载的定义与原因详解 什么是函数重载 函数重载是指在一个类中定义多个同名函数,但它们的参数不同(参数个数、参数类型或参数顺序不同)。 函数重载的定义 函数名必须相同。 函数的参数列表必须不同(个数、类型、顺序)。 函数的返回类型可以不同。 可以发生在同一个 class 内,也可以发生在父类和子类之间。 编译器根据函数参数列表的不同自动进行函数匹配。…

    C 2023年5月23日
    00
  • c++中的malloc底层实现代码

    C++中的malloc是一种动态内存分配的方式,它可以在程序运行期间动态分配所需的内存大小,以满足程序运行时所需的空间需求。malloc的底层实现需要非常细致的代码编写,以下就介绍其详细实现攻略及示例说明。 1. malloc底层实现攻略 malloc的底层实现需要在内存管理器中调用操作系统API获取更多的内存空间,然后通过算法来管理这些空间的分配和释放。下…

    C 2023年5月23日
    00
  • win10快捷方式图标异常怎么办?

    当win10快捷方式图标异常时,可以尝试以下解决方法: 方法一:重新建立图标缓存 按下Win + R键组合键打开运行窗口,输入cmd,按下Ctrl+Shift+Enter组合键,以管理员身份运行命令提示符。 在命令提示符窗口中,输入以下命令并按下回车键:taskkill /f /im explorer.exe。 等待至桌面中的所有图标消失,继续在命令提示符窗…

    C 2023年5月23日
    00
  • linux多线程编程(四)

    Linux多线程编程(四)攻略 前言 本文将讲解在Linux环境下进行多线程编程的基本概念、操作方法和注意事项,通过示例代码演示实现多线程的一些常见用法。 基础知识 线程的创建和销毁 线程是轻量级的进程,一个进程可以包含多个线程。线程的创建和销毁都是通过pthread库中的函数来完成的: #include <pthread.h> int pthr…

    C 2023年5月22日
    00
  • 荣耀畅玩7c怎么截长屏?荣耀畅玩7c滚动截屏教程

    荣耀畅玩7c怎么截长屏? 在荣耀畅玩7c中,想要截取整个长页面时,需要使用滚动截屏的功能。下面是具体的操作步骤: 打开你需要截屏的页面,滚动到页面最顶部; 按下电源键和音量减键同时按住,直到屏幕闪一下; 这时候就已经完成了第一张截屏,继续向下滚动,直到滑动到要截屏的最下面的部分; 继续按下电源键和音量减键同时按住,直到屏幕闪一下,即可完成整个页面的截屏。 需…

    C 2023年5月23日
    00
  • Go错误和异常CGO fallthrough处理教程详解

    Go错误和异常CGO fallthrough处理教程详解 异常和错误的区别 在Go语言中,没有类似于Java的异常处理机制,而是采用了错误处理机制。Go语言中的错误是一种可以提前预判到的普通值,包含了自定义的错误信息。与其他语言不同,Go语言中的错误处理是基于返回值的,而不是异常。 如何处理错误 在Go语言中,一个函数的返回值通常由一个值和一个错误组成。当函…

    C 2023年5月23日
    00
  • C++编程面向对象入门全面详解

    C++编程面向对象入门全面详解攻略 本篇攻略旨在为初学者提供C++编程中面向对象的入门指南,以及相关基础概念的详细介绍。 面向对象编程的概念 面向对象编程是一种程序设计范型,它将现实世界中的事物抽象、封装为类,并通过类之间的继承、组合等关系,对这些事物进行描述和操作。 在C++中,面向对象编程主要包含以下几个方面: 类(Class):描述某一类事物的数据和行…

    C 2023年5月22日
    00
  • 使用批处理文件异地备份数据库(最近几天的数据)

    下面是使用批处理文件异地备份数据库(最近几天的数据)的完整攻略: 第一步:编写批处理文件 打开文本编辑器,新建一个批处理文件,后缀名为“.bat”。比如,我们可以用“backup.bat”来命名这个文件。 在批处理文件中输入以下代码: @echo off REM 配置数据库备份参数 set backup_path=D:\Backup\Database set…

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