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

yizhihongxing

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

什么是线程池?

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

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++编译器集成到VC2005中的方法

    将其他C/C++编译器集成到VC2005中,可以方便地拥有多个编译器的优势,使得编译更加高效、灵活,并且可以在不同的开发环境中进行快速切换。 下面是把其他C/C++编译器集成到VC2005中的方法: 第一步:获取其他编译器 在集成其他编译器之前,必须首先获取相应的编译器。常见的C/C++编译器有GCC、Clang、Intel C++等。在此以获取GCC为例,…

    C 2023年5月23日
    00
  • 深入讲解数据库中Decimal类型的使用以及实现方法

    下面我来给您详细讲解“深入讲解数据库中Decimal类型的使用以及实现方法”的完整攻略。 深入讲解数据库中Decimal类型的使用以及实现方法 什么是Decimal类型 Decimal类型是一种用于存储精确数值的数据类型。在数据库中常用于存储货币、利率、精度要求高的计算结果等场景。与浮点型(float)不同的是,Decimal类型不会存在精度损失的情况。 D…

    C 2023年5月22日
    00
  • C语言实现的猴子偷桃之类算法

    C语言实现的猴子偷桃之类算法 算法思路 猴子偷桃是一个经典的算法问题,其思路如下: 有一堆桃子,猴子第一天吃掉一半,发现还不过瘾,就又吃了一个;第二天又吃掉剩下的一半,发现还不过瘾,又吃了一个;以后每天都这样吃,直到最后只剩一个桃子为止。求原来有多少桃子。 为了方便解题,我们可以反向思考,即从最后一天向前推断。假设在第N天时只剩下一个桃子,那么在第N-1天时…

    C 2023年5月22日
    00
  • notepad介绍及插件cmake编译过程(替代notepad++)

    Notepad介绍 Notepad是一款Windows平台下的简洁文本编辑器,其主要功能包括查看、编辑纯文本文档,具有简单、易上手、快速启动等特点,在Windows操作系统服务管理、配置文件编辑、读写脚本等工作中得到了广泛的应用。 插件CMake编译过程 Notepad++是Notepad的增强版,其功能更为强大,支持丰富的插件,其中之一就是CMake,它能…

    C 2023年5月23日
    00
  • C++如何实现二叉树链表

    C++可以通过定义结构体来表示二叉树链表节点,结构体中包含左右子节点指针和数据域。通过指针来实现二叉树的构建和遍历。 以下是具体的实现步骤: 1. 定义结构体 首先我们需要定义一个结构体来表示二叉树链表节点,结构体定义如下: struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNod…

    C 2023年5月23日
    00
  • C语言循环结构详解

    C语言循环结构详解 什么是循环结构? 循环结构是计算机编程语言中最重要的结构之一,它允许程序重复执行一次或多次某个代码块。 在C语言中,循环结构主要有以下三种: for循环结构 while循环结构 do…while循环结构 for循环结构 for循环结构是最常用的循环结构之一,在需要重复执行N次的情况下,使用for循环比较便捷。 for循环结构的语法格式…

    C 2023年5月23日
    00
  • 何时喝水一些分析

    何时喝水一些分析 喝水是我们日常生活中非常重要的一项习惯,正确的喝水方式和时间也会对我们的身体健康产生积极影响。本文将从几个方面来分析何时喝水最为合适。 饮食时间 饮食时间是影响我们喝水的一个重要因素。如果在饭前喝水会引起胃酸稀释,影响胃中消化酶的活性,导致吸收不良。因此,在饭前应该尽量避免喝水。但是,如果已经喝饱水,可以等待30分钟再吃饭。 而在饭后喝水则…

    C 2023年5月23日
    00
  • SQL Server 作业的备份(备份作业非备份数据库)

    SQL Server 作业备份攻略 前提条件 在进行 SQL Server 作业备份前,需要先满足以下条件: 必须有 SQL Server Management Studio(SSMS)的访问权限; 必须有 SQL Server Agent 的管理权限; 确保备份作业未选中要备份的数据库。 备份作业 SQL Server 的作业(job)是一种定时运行的任务…

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