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__
相关推荐
大模型铲屎官10 分钟前
【Python-Day 14】玩转Python字典(上篇):从零开始学习创建、访问与操作
开发语言·人工智能·pytorch·python·深度学习·大模型·字典
yunvwugua__13 分钟前
Python训练营打卡 Day27
开发语言·python
Java致死1 小时前
设计模式Java
java·开发语言·设计模式
zh_xuan1 小时前
c++ 类的语法3
开发语言·c++
一律清风2 小时前
【Opencv】canny边缘检测提取中心坐标
c++·opencv
belldeep5 小时前
如何阅读、学习 Tcc (Tiny C Compiler) 源代码?如何解析 Tcc 源代码?
c语言·开发语言
LuckyTHP5 小时前
java 使用zxing生成条形码(可自定义文字位置、边框样式)
java·开发语言·python
a东方青6 小时前
蓝桥杯 2024 C++国 B最小字符串
c++·职场和发展·蓝桥杯
XiaoyaoCarter8 小时前
每日一道leetcode
c++·算法·leetcode·职场和发展·二分查找·深度优先·前缀树
Blossom.1188 小时前
使用Python实现简单的人工智能聊天机器人
开发语言·人工智能·python·低代码·数据挖掘·机器人·云计算