C++ 并发编程指南(13)线程池原理与实践

文章目录

前言:

在多线程编程中,线程池是一种常用的技术,它可以有效地管理和调度线程。C++标准库中的和头文件提供了对线程池的支持。本文将介绍C++线程池的机制和原理。

一、线程池原理与实践

1、线程池的概念

线程池是一种线程使用模式,它通过预先创建多个线程,将任务提交给线程池,由线程池负责调度和执行任务。线程池的主要作用是减少线程创建和销毁的开销,提高程序的并发性能

2、线程创建和销毁的开销有哪些?

线程创建与销毁的开销主要体现在以下几个方面:

2.1、系统资源消耗

  • 内存分配:线程的创建需要为其分配一定的内存空间,以存储线程栈、线程控制块等数据结构。这些内存空间的分配和回收都需要消耗系统资源。
  • 内核对象:在操作系统层面,线程的创建会涉及到内核对象的创建,这些对象用于管理线程的状态、优先级、调度等信息。同样地,线程的销毁也需要销毁这些内核对象。

2.2、初始化与清理

  • 初始化:线程的创建需要进行一系列的初始化操作,如设置线程栈、初始化线程控制块等。这些操作都需要消耗一定的计算资源。
  • 清理:线程的销毁需要执行一系列的清理操作,如释放线程栈、销毁线程控制块等。这些操作同样需要消耗计算资源。

2.3、上下文切换

  • 当一个线程被创建或销毁时,操作系统需要进行上下文切换。上下文切换涉及到保存当前线程的上下文(如寄存器值、栈信息等)和加载新线程的上下文。这个过程需要消耗一定的CPU时间。

2.4、性能影响

  • 创建开销:线程的创建通常比进程的创建要快,但在某些操作系统上,线程的创建仍然是一个相对昂贵的操作。例如,在Solaris 2操作系统上,创建进程的时间大约是创建线程的30倍。
  • 销毁开销:线程的销毁同样需要一定的开销,特别是当线程被频繁创建和销毁时,这种开销会更加明显。

2.5、线程池的优势

  • 减少开销:线程池通过预先创建一定数量的线程,并在需要时复用这些线程来执行任务,从而避免了频繁创建和销毁线程的开销。
  • 提高性能:线程池通过限制并发线程的数量和有效地管理线程资源,可以提高系统的性能和资源利用率。

总结

线程创建与销毁的开销主要包括系统资源消耗、初始化与清理、上下文切换以及性能影响等方面。这些开销可以通过使用线程池等技术来降低,从而提高系统的整体性能和资源利用率。

3、线程池的机制

C++线程池主要由以下几个部分组成:

  • 任务队列:用于存放待执行的任务。这些任务通常以函数对象、函数指针或可调用对象的形式存在。
  • 线程集合:包含了一定数量的工作线程,这些线程会不断地从任务队列中取出任务并执行。
  • 同步机制:用于协调线程之间的任务分配和状态同步。常见的同步机制包括互斥锁(mutex)、条件变量(condition variable)等。
  • 线程管理:包括线程的创建、销毁、启动和停止等管理操作。为了优化性能,线程池通常会限制线程的最大数量,避免过多的线程创建和销毁开销。
cpp 复制代码
#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__

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

class NoneCopy {

public:
    ~NoneCopy(){}
protected:
    NoneCopy(){}
private:
    NoneCopy(const NoneCopy&) = delete;
    NoneCopy& operator=(const NoneCopy&) = delete;
};

class ThreadPool : public NoneCopy {
public:
    //继承基类NoneCopy就不需要写如下删除了
    //ThreadPool(const ThreadPool&) = delete;
    //ThreadPool& operator=(const ThreadPool&) = delete;

    static ThreadPool& instance() {
        static ThreadPool ins;
        return ins;
    }

    using Task = std::packaged_task<void()>;


    ~ThreadPool() {
        stop();
    }

    template <class F, class... Args>
    auto commit(F&& f, Args&&... args) ->
        std::future<decltype(std::forward<F>(f)(std::forward<Args>(args)...))> {
        using RetType = decltype(std::forward<F>(f)(std::forward<Args>(args)...));
        if (stop_.load())
            return std::future<RetType>{};

        auto task = std::make_shared<std::packaged_task<RetType()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...));

        std::future<RetType> ret = task->get_future();
        {
            std::lock_guard<std::mutex> cv_mt(cv_mt_);
            tasks_.emplace([task] { (*task)(); });
        }
        cv_lock_.notify_one();
        return ret;
    }

    int idleThreadCount() {
        return thread_num_;
    }

private:
    ThreadPool(unsigned int num = std::thread::hardware_concurrency())
        : stop_(false) {
            {
                /*
                if (num <= 1)
                    thread_num_ = 2;
                else
                    thread_num_ = num;
                    */
                thread_num_ = 10;
            }
            start();
    }
    
    void start() {
        for (int i = 0; i < thread_num_; ++i) {
            pool_.emplace_back([this]() {
                while (!this->stop_.load()) {
                    Task task;
                    {
                        std::unique_lock<std::mutex> cv_mt(cv_mt_);
                        this->cv_lock_.wait(cv_mt, [this] {
                            return this->stop_.load() || !this->tasks_.empty();
                            });
                        if (this->tasks_.empty())
                            return;

                        task = std::move(this->tasks_.front());
                        this->tasks_.pop();
                    }
                    this->thread_num_--;
                    task();
                    this->thread_num_++;
                }
                });
        }
    }
    
    void stop() {
        stop_.store(true);
        cv_lock_.notify_all();
        for (auto& td : pool_) {
            if (td.joinable()) {
                std::cout << "join thread " << td.get_id() << std::endl;
                td.join();
            }
        }
    }

private:
    std::mutex               cv_mt_;
    std::condition_variable  cv_lock_;
    std::atomic_bool         stop_;
    std::atomic_int          thread_num_;
    std::queue<Task>         tasks_;
    std::vector<std::thread> pool_;
};

#endif  // !__THREAD_POOL_H__
相关推荐
余~~185381628006 分钟前
稳定的碰一碰发视频、碰一碰矩阵源码技术开发,支持OEM
开发语言·人工智能·python·音视频
蜀黍@猿40 分钟前
【C++ 基础】从C到C++有哪些变化
c++
Am心若依旧40941 分钟前
[c++11(二)]Lambda表达式和Function包装器及bind函数
开发语言·c++
明月看潮生43 分钟前
青少年编程与数学 02-004 Go语言Web编程 20课题、单元测试
开发语言·青少年编程·单元测试·编程与数学·goweb
zh路西法1 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(一):从电梯出发的状态模式State Pattern
c++·决策树·状态模式
大G哥1 小时前
java提高正则处理效率
java·开发语言
VBA63371 小时前
VBA技术资料MF243:利用第三方软件复制PDF数据到EXCEL
开发语言
轩辰~1 小时前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
小_太_阳1 小时前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
向宇it1 小时前
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
开发语言·unity·c#·编辑器·游戏引擎