首页 > 编程语言 >c++线程池(二)——线程池优化

c++线程池(二)——线程池优化

时间:2024-03-19 13:58:20浏览次数:30  
标签:std int c++ threadIndex 线程 include 优化 pool

文章目录

概要

增加扇入扇出:
优化:子线程维护自己的本地队列

分析:
目前文章《线程池一》介绍了一个简单的线程池,存在多个线程同时访问一个任务队列Task,出现抢锁的情况,这样会存在一定的性能消耗,会导致有些没抢到任务的线程没事做,造成资源浪费。
实现:
为每个线程创建一个任务队列,让每个线程维护自己的任务队列,通过生产者生产的任务,依次分发到各个队列中,避免了多个线程去抢一个锁。

整体架构流程

线程池代码实现

//<ThreadPool.h>
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <functional>
#include <random>
#include <chrono>
#include <windows.h>
 //任务类型,这里简单地使用 std::function<void()>
using Task = std::function<void()>;

class ThreadPool {
public:
    ThreadPool(int threadNumbers)
        :max_threads(threadNumbers), 
        startTime(std::chrono::high_resolution_clock::now()),
        LocalMutex(threadNumbers), 
        LocalTask(threadNumbers),
        stop(threadNumbers),
        cond_varVec(threadNumbers)
    {
        for (int i = 0; i < threadNumbers; i++)
        {
            workers.emplace_back(WorkerThread, this, i);
        }
    }
    ~ThreadPool()
    {
        {
            for (size_t i = 0; i < max_threads; i++)
            {
                std::unique_lock<std::mutex> lock(LocalMutex[i]);
                stop[i] = true;
                cond_varVec[i].notify_all(); // 通知所有工作线程停止
            }

        }
        for (std::thread& worker : workers) {
            worker.join();
        }
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime);
        std::cout << "Total time: " << duration.count() << " ms" << std::endl;
    }

    // 添加新任务到线程池
    void AddTask(Task task, int hashIndex);
private:
    //线程的工作函数
    static void WorkerThread(void* arg,int threadIndex);

private:
    std::vector<std::thread> workers;                               // 工作线程池
    std::vector<std::condition_variable> cond_varVec;               // 条件变量,用于通知工作线程有新任务
    int max_threads;                                                // 线程池的最大线程数
    std::vector<bool> stop;                                         // 线程池停止标志
    std::vector<std::deque<Task>>  LocalTask;                       // 线程内部维护的任务队列
    std::vector<std::mutex> LocalMutex;                             // 子线程内部的互斥锁
    std::chrono::high_resolution_clock::time_point startTime;
};
//-------------<ThreadPool.cpp>------------------
#include "ThreadPool.h"
void  ThreadPool::AddTask(Task task,int hashIndex) {
    {
        std::unique_lock<std::mutex> lock(LocalMutex[hashIndex]);
        LocalTask[hashIndex].push_back(task);
        //tasks.push(task);
    }
    cond_varVec[hashIndex].notify_one(); // 通知一个工作线程有新任务
}


void ThreadPool::WorkerThread(void* arg, int threadIndex)
{
    ThreadPool* pool = (ThreadPool*)arg;
    while (true)
    {
        std::unique_lock<std::mutex>lock(pool->LocalMutex[threadIndex]);
        pool->cond_varVec[threadIndex].wait(lock, [pool, threadIndex] { return pool->stop[threadIndex] || !pool->LocalTask[threadIndex].empty(); });
        if (pool->stop[threadIndex] && pool->LocalTask[threadIndex].empty()) {
            break;
        }
        Task task = pool->LocalTask[threadIndex].front();
        pool->LocalTask[threadIndex].pop_front();
        task(); // 执行任务
    }

}

技术细节

在《线程池一》的基础上,我修改了任务队列,将queue改成了类型为deque的数组,每个队列都用自己的互斥锁与condition_variable。

小结

测试场景一:五百个任务,四个线程去执行

int main() {

    const int num_tasks = 500;
    const int num_threads = 4;

    ThreadPool pool(num_threads);

    // 添加任务到线程池
    for (int i = 0; i < num_tasks; ++i) {
        int hashIndex = i % num_threads;
        pool.AddTask(std::bind(DoWork, i), hashIndex);
    }


    return 0;
}

线程池(一)耗时
在这里插入图片描述
线程池(二)耗时

在这里插入图片描述

标签:std,int,c++,threadIndex,线程,include,优化,pool
From: https://blog.csdn.net/qq452562499/article/details/136804126

相关文章

  • 06_C++多维数组
    多维数组,数组指针在二维数组上的应用。#include<iostream>#include<stdio.h>usingnamespacestd;intmain(){intarr[3][5]={{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};int(*p)[5]=arr;cout<<"*p:"<<*p<<endl......
  • 多线程(一)
    1、线程与进程进程:一个正在执行的程序,是资源分配的最小单位1)进程中的事情需要按照一定的顺序逐个执行,那么如何让一个进程中的一些事情同时执行?2)进程出现了很多弊端:一是由于进程是资源拥有者,创建、撤销与切换存在较大的时空开销,因此需要引入轻量级进程;二是由于多处理器(SMP)出现,可......
  • 数据库查询优化:解析不使用索引的场景及对策
    数据库索引的目的是为了加快查询速度,但在某些情况下,查询可能不会使用索引,即所谓的“不走索引”。以下是一些可能导致数据库查询不使用索引的情况:全表扫描:当查询条件中的字段没有建立索引,或者查询的条件是对整个表进行范围查询(如使用 BETWEEN),数据库可能会选择全表扫描而不是使......
  • 分月饼【华为OD机试JAVA&Python&C++&JS题解】
    一.题目-分月饼中秋节,公司分月饼,m个员工,买了n个月饼,m<=n,每个员工至少分1个月饼,但可以分多个,单人分到最多月饼的个数是Max1,单人分到第二多月饼个数是Max2,Max1-Max2<=3,单人分到第n-1多月饼个数是Max(n-1),单人分到第n多月饼个数是Max(n),Max(n-1)–Max(n)<=3,问有多少......
  • C++实现欧拉筛法
    Euler筛法介绍以筛出100以内(含100)的所有素数为例来说明一下欧拉筛法的原理。和Eratosthenes筛法一样,Euler筛法也从2开始筛,但Eratosthenes筛法会把2的倍数一批全部筛掉,而Euler筛法用2筛时仅仅把2*2(即4)筛掉,而把其它偶数留到后面再筛掉,从而避免了一个偶数被多次筛除带来的性能开销,......
  • JAVA多线程常用方法
    文章目录1.常用方法总结2.run和start3.sleep4.yield5.join6.interrupt6.1相关方法6.2打断park线程6.3过时方法7.守护线程8.线程状态8.1操作系统中8.2Java中1.常用方法总结Thread类的常用API如下:方法说明publicvoidstart()启动一个新线程,Java虚拟......
  • C++STL第五篇(链表List的使用方法)
    list链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。相......
  • C++刷题杂记
    目录C++中如何声明二维vectorC++中如何声明二维vector在C++中,你可以使用嵌套的std::vector来声明一个二维的vector。每个元素本身是一个std::vector,而这些元素的集合构成了外部的std::vector。以下是如何声明一个二维vector的示例:#include<vector>intmain(){//声......
  • 基于多种优化算法的物联网无人机基站研究【布谷鸟搜索CS、大象群体优化EHO、灰狼优化G
     ......
  • C++类实现顺序表
    环境:vscodesequencelist.h#ifndefSEQUENCELIST_H#defineSEQUENCELIST_H#defineMAXSIZE20//最大存储容量typedefintElemType;classSqList{public:SqList();//SqList(ElemTypeelems[],intn);//有参构造器~SqLis......