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语言 strncpy()函数

    下面是关于 C 语言中 strncpy() 函数的详细使用攻略: 一、函数简介 strncpy() 函数是 C 语言中的字符串复制函数,它可以复制指定长度的字符串,并返回目标字符串地址。 函数原型如下: char* strncpy(char* dest, const char* src, size_t n); 参数说明:- dest:目标字符串,拷贝后的字符…

    C 2023年5月9日
    00
  • Linux中使用VS Code编译调试C++项目详解

    下面我将详细讲解如何在Linux中使用VS Code编译调试C++项目。 准备工作 安装VS Code 首先,我们需要安装一个文本编辑器,这里我们选择VS Code。可以到官网下载 Visual Studio Code。 下载完成后,解压安装文件并将其保存在可执行路径中(例如/usr/local/bin),使其能够在终端中运行。 安装C++编译器 接下来,我…

    C 2023年5月23日
    00
  • C语言魔塔游戏的实现代码

    下面详细讲解一下C语言魔塔游戏的实现代码的攻略。 一、准备工作 安装开发环境需要在计算机上安装C语言开发环境,比如Visual Studio、Code::Blocks等。 了解游戏规则在编写代码之前,需要了解魔塔游戏的规则和玩法。 二、游戏地图设计 设置地图的大小和格子可以使用二维数组来表示地图,数组的行和列表示地图的大小,数组中的每个元素表示地图中的一个格…

    C 2023年5月24日
    00
  • 使用C语言实现vector动态数组的实例分享

    下面是使用C语言实现vector动态数组的完整攻略: 什么是vector动态数组 vector是一种动态数组,随着数据的增加,容器动态扩展。vector和数组很相似,但是有个重要的优点,那就是可以动态扩展,放置溢出问题。不过,vector并不是一个内置的C语言数据类型,需要我们通过编程实现。 思路概述 实现一个vector动态数组主要涉及两个方面:存储数据和…

    C 2023年5月23日
    00
  • Java程序与C语言的区别浅析

    Java程序与C语言的区别浅析 相同点 Java程序和C语言程序都是计算机程序。两者都需要编译成计算机能够识别的二进制代码后才能执行。Java程序和C语言程序都需要按照指定的语法规则书写程序,并且它们都需要语言自带的IDE或编译器进行编写语法检查、编译等操作。 不同点 语法 Java程序与C语言的基本语法有较大差异。C语言程序中常用的指针操作、预处理器等在J…

    C 2023年5月30日
    00
  • 使用C++一步步实现俄罗斯方块

    使用C++一步步实现俄罗斯方块的完整攻略 什么是俄罗斯方块 俄罗斯方块(Tetris)是一款经典的电子游戏,最早由苏联程序员Alexey Pajitnov于1984年创造。它的玩法非常简单,玩家需要控制不同形状的积木,让它们在游戏界面中形成一行,然后这一行就会消失,玩家可以得到相应的分数。如果积木堆满了整个屏幕,游戏就会结束。 如何使用C++实现俄罗斯方块 …

    C 2023年5月23日
    00
  • C语言流程控制之switch语句详解

    C语言流程控制之switch语句详解是本网站总结的一篇C语言教程文章,主要介绍了switch语句的用法和注意事项。本文将通过以下几个方面详细讲解: 1. switch语句的基本格式 switch语句由一个表达式和多个case组成,如下所示: switch(expression){ case constant-expression1: statement1; …

    C 2023年5月23日
    00
  • C指针声明

    C指针是C语言中非常重要的一个概念,用于处理内存地址和变量的数据类型,因此在编写C程序时,使用正确的指针声明会在代码性能和可读性方面产生重要影响。以下是C指针声明的完整使用攻略。 什么是指针声明 在C语言中,指针是一个存储变量地址的变量。在声明指针变量时,需要指定指针所指向的变量的类型。指针的声明方式在语法上与变量的声明类似,但是需要在类型前面添加一个星号(…

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