5.5、线程池同步机制类封装及线程池实现

news2024/12/23 6:00:02

代码地址

5.5、线程池同步机制类封装及线程池实现

  • 1.线程池
  • 2.代码实现
    • ①锁
      • Ⅰ、locker.h
      • Ⅱ、locker.cpp
    • ②条件变量
      • Ⅰ、cond.h
      • Ⅱ、cond.cpp
    • ③信号量
      • Ⅰ、sem.h
      • Ⅱ、sem.cpp
    • ④线程池
      • Ⅰ、threadpool.h
      • Ⅱ、threadpool.cpp

1.线程池

线程池是由服务器预先创建的一组子线程,线程池中的线程数量应该和 CPU 数量差不多。线程池中的所有子线程都运行着相同的代码。当有新的任务到来时,主线程将通过某种方式选择线程池中的某一个子线程来为之服务。相比与动态的创建子线程,选择一个已经存在的子线程的代价显然要小得多。至于主线程选择哪个子线程来为新任务服务,则有多种方式:

  • 主线程使用某种算法来主动选择子线程。最简单、最常用的算法是随机算法和 Round Robin(轮流选取)算法,但更优秀、更智能的算法将使任务在各个工作线程中更均匀地分配,从而减轻服务器的整体压力。
  • 主线程和所有子线程通过一个共享的工作队列来同步,子线程都睡眠在该工作队列上。当有新的任务到来时,主线程将任务添加到工作队列中。这将唤醒正在等待任务的子线程,不过只有一个子线程将获得新任务的”接管权“,它可以从工作队列中取出任务并执行之,而其他子线程将继续睡眠在工作队列上。
    在这里插入图片描述

线程池中的线程数量最直接的限制因素是中央处理器(CPU)的处理器(processors/cores)的数量N :如果你的CPU4-cores的,对于CPU密集型的任务(如视频剪辑等消耗CPU计算资源的任务)来说,那线程池中的线程数量最好也设置为4(或者+1防止其他因素造成的线程阻塞);对于IO密集型的任务,一般要多于CPU的核数,因为线程间竞争的不是CPU的计算资源而是IOIO的处理一
般较慢,多于cores数的线程将为CPU争取更多的任务,不至在线程处理IO的过程造成CPU空闲导致资源浪费。

  • 空间换时间,浪费服务器的硬件资源,换取运行效率。
  • 池是一组资源的集合,这组资源在服务器启动之初就被完全创建好并初始化,这称为静态资源。
  • 当服务器进入正式运行阶段,开始处理客户请求的时候,如果它需要相关的资源,可以直接从池中获取,无需动态分配。
  • 当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用释放资源。

2.代码实现

①锁

Ⅰ、locker.h

#ifndef LOCK_H
#define LOCK_H

#include <pthread.h>

class Locker {
private:
    // 互斥锁
    pthread_mutex_t m_mutex;

public:
    Locker();
    ~Locker();

    // 加锁
    bool lock();

    // 解锁
    bool unlock();

    // 获取锁;
    pthread_mutex_t * get();

};

#endif

Ⅱ、locker.cpp

#include "locker.h"
#include <exception>

// 构造函数,初始化 m_mutex
Locker::Locker() {
    if (pthread_mutex_init(&m_mutex, NULL) != 0) {
        throw std::exception();
    }
}


// 析构函数,销毁 m_mutex
Locker::~Locker() {
    pthread_mutex_destroy(&m_mutex);
}


// 给 m_mutex 加锁
bool Locker::lock() {
    return pthread_mutex_lock(&m_mutex) == 0;
}


// 解开 m_mutex 的锁
bool Locker::unlock() {
    return pthread_mutex_unlock(&m_mutex) == 0;
}

// 获取 m_mutex
pthread_mutex_t * Locker::get() {
    return &m_mutex;
}

②条件变量

Ⅰ、cond.h

#ifndef COND_H
#define COND_H

#include <pthread.h>
#include <time.h>


class Cond {

private:

    // 条件变量
    pthread_cond_t m_cond;
    

public:
    Cond();
    ~Cond();

    // 阻塞等待唤醒
    bool wait(pthread_mutex_t *);

    // 超时等待
    bool timedwait(pthread_mutex_t *, timespec);

    // 唤醒单个等待条件
    bool signal();

    // 唤醒全部等待条件
    bool broadcast();

};

#endif

Ⅱ、cond.cpp

#include "cond.h"
#include <exception>


// 构造函数,初始化 m_cond
Cond::Cond() {
    if (pthread_cond_init(&m_cond, NULL) != 0) {
        throw std::exception();
    }
}


// 销毁 m_cond
Cond::~Cond() {
    pthread_cond_destroy(&m_cond);
}

// 条件阻塞等待唤醒 m_cond
bool Cond::wait(pthread_mutex_t * mutex) {
    return pthread_cond_wait(&m_cond, mutex) == 0;
}



// 超时等待条件阻塞
bool Cond::timedwait(pthread_mutex_t * mutex, timespec t) {
    return pthread_cond_timedwait(&m_cond, mutex, &t) == 0;
}



// 唤醒等待进程
bool Cond::signal() {
    return pthread_cond_signal(&m_cond) == 0;
}


// 唤醒全部进程
bool Cond::broadcast() {
    return pthread_cond_broadcast(&m_cond) == 0;
}

③信号量

Ⅰ、sem.h

#ifndef SEM_H
#define SEM_H

#include <semaphore.h>

class Sem {
private:
    // 信号量
    sem_t m_sem;

public:
    Sem();
    Sem(int);
    ~Sem();

    // 信号量的加锁,如果为0,就阻塞,调用一个减 1 
    bool wait();

    // 调用一次 + 1
    bool post();

};


#endif

Ⅱ、sem.cpp

#include "sem.h"
#include <exception>


// 构造函数
Sem::Sem() {
    if (sem_init(&m_sem, 0, 0) != 0) {
        throw std::exception();
    }
}


// 构造函数带初始值 num
Sem::Sem(int num) {
    if (sem_init(&m_sem, NULL, num) != 0) {
        throw std::exception();
    }
}


// 析构函数,销毁信号量 m_sem
Sem::~Sem() {
    sem_destroy(&m_sem);
}

// 阻塞信号量,数值 -1
bool Sem::wait() {
    return sem_wait(&m_sem) == 0;
}

// 增加信号的值,+1
bool Sem::post() {
    return sem_post(&m_sem) == 0;
}

④线程池

Ⅰ、threadpool.h

#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <list>
#include "locker.h"
#include "sem.h"
#include "cond.h"

// 线程池
template<class T>
class ThreadPool {

private:

    // 线程的数量
    int m_thread_number;

    // 线程数组,大小为m_thread_number
    pthread_t * m_threads;

    // 允许等待的最大数量
    int m_max_requests;

    // 请求队列,需要处理的任务
    std::list<T*> m_workqueue;

    // 请求队列的互斥锁
    locker m_queuelocker;

    // 是否有任务需要处理,信号量的数量
    sem m_queuestat;

    // 是否结束进程
    bool m_stop;

    
public:
    //thread_number是线程池中线程的数量
    //max_requests是请求队列中最多允许的、等待处理的请求的数量
    ThreadPool(int thraed_number = 8, int max_requests = 10000);

    // 析构函数,销毁线程的数据
    ~ThreadPool();

    // 增加任务进工作队列中
    bool append(T *);

private:
    // 创建线程之后的运行函数
    void * worker(void * arg);

    // 取出队列中任务,不断的运行线程处理任务
    void run();

};

#endif

Ⅱ、threadpool.cpp

#include "threadpool.h"
#include <iostream>

// 线程池的构造函数
template<class T>
ThreadPool<T>::ThreadPool(int thread_numebr, int max_requests) {

    // 传入错误的参数
    if (thread_number <= 0 || max_requests <= 0) {
        throw std::exception();
    }


    // 属性赋值
    m_thread_number = thread_numebr;
    m_max_requests = max_requests;
    m_stop = false;
    m_queuelocker = locker();
    m_queuestat = sem();
    m_workqueue.clear();

    // 创建线程数组
    m_threads = new pthread_t[m_thread_number];

    if (m_threads == nullptr) {
        throw std::exception();
    }

    // 创建线程
    for (int i = 0; i < m_thread_number; i ++ ) {
        printf("create the %dth thread\n", i);

        // 如果创建失败返回
        if (pthread_create(m_threads[i], nullptr, worker, this) != 0) {
            delete[] m_threads;
            throw std::exception();
        }

        // 创建线程分离
        if (pthread_detach(m_threads[i]) != 0) {
            delete[] m_threads;
            throw std::exception();
        }

    }
}


// 线程池的析构函数,销毁一些变量
template<class T>
ThreadPool<T>::~ThreadPool() {
    delete[] m_threads;
    m_stop = true;
    m_workqueue.clear();
}


template<class T>
bool ThreadPool<T>::append(T * requests) {

    // 操作工作队列时一定要加锁,因为它被所有线程共享。
    m_queuelocker.lock();
    if (m_workqueue.size() >= m_max_requests) {
        m_queuelocker.unlock();
        return false;
    }

    m_workqueue.push_back(requests);
    m_queuelocker.unlock();
    m_queuestat.post();
    return true;

}


// 创建的线程需要运行的函数
template<class T>
void * ThreadPool<T>::worker(void * arg) {

    ThreadPool * p = (ThreadPool *) arg;
    p->run();

    return p;
}


// 线程池处理函数
template<class T>
void ThreadPool<T>::run() {

    while (!m_stop) {
        // 信号量不为0,可以运行
        m_queuestat.wait();
        m_queuelocker.lock();

        // 获取队列第一个请求
        T * requests = m_workqueue.front();
        // 第一个请求已经取出执行,可以pop掉
        m_workqueue.pop_front();

        // 解锁
        m_queuelocker.unlock();
        if (requests == nullptr) {
            continue;
        }

        // 请求的处理
        requests->process();
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/449664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

行为识别预警系统 opencv

行为识别预警系统通过pythonopencv网络模型技术&#xff0c;行为识别预警系统对现场画面中人的行为进行智能分析&#xff0c;发现违规行为自动抓拍告警&#xff0c;同步回传后台存档提醒值班人员及时处理。OpenCV的全称是Open Source Computer Vision Library&#xff0c;是一个…

CVE漏洞复现-CVE-2019-5736 Docker逃逸

CVE-2019-5736 Docker逃逸 Docker是什么&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有…

JAVA实现AES加密、解密附代码

大家好&#xff0c;今天我们一起来学习 Java中的加密解密技术&#xff0c; java是一门面向对象的语言&#xff0c;使用它的开发人员都是非常聪明的人&#xff0c;他们对数据的安全性要求很高。在实际的应用中&#xff0c;我们都需要对数据进行加密、解密处理&#xff0c;这在 j…

【数据结构】第十三站:排序(上)

本文目录 一、排序概念二、插入排序1.插入排序的基本思想2.算法实现3.时间复杂度分析 三、希尔排序1. 希尔排序的思想2.希尔排序的代码实现3.希尔排序和插入排序的性能测试比较4.希尔排序的时间复杂度 四、直接选择排序1.基本思想2.直接选择排序的步骤3.直接选择排序的代码实现…

接口自动化测试用例如何设计?

说到自动化测试&#xff0c;或者说接口自动化测试&#xff0c;多数人的第一反应是该用什么工具&#xff0c;比如&#xff1a;Python Requests、Java HttpClient、Apifox、MeterSphere、自研的自动化平台等。大家似乎更关注的是哪个工具更优秀&#xff0c;甚至出现“ 做平台的 &…

音视频开发开发核心知识+小白入门必看基础知识

音视频开发是一个广泛的领域&#xff0c;它涉及到多个技术领域&#xff0c;包括音频编解码、视频编解码、媒体容器格式、流媒体传输、音视频处理等。以下是音视频开发的一些基础知识&#xff1a; 音频编解码器&#xff1a;音频编解码器是将数字音频信号编码成一种压缩格式&…

什么原因会导致香港轻量云服务器运行缓慢?

对于外贸企业主来说&#xff0c;想要为自己的网站选择一个可靠的主机供应商&#xff0c;并考虑使用香港轻量云服务器&#xff0c;则本文是必读的。本文将探讨租用香港轻量云服务器后遇到的运行缓慢的问题。下文这些因素被视为其中的主要因素。仔细分析它们中的每一个将帮助您确…

华为OD机试真题(Java),5键键盘的输出(100%通过+复盘思路)

一、题目描述 有一个特殊的5键键盘&#xff0c;上面有a&#xff0c;ctrl-c&#xff0c;ctrl-x&#xff0c;ctrl-v&#xff0c;ctrl-a五个键。 a键在屏幕上输出一个字母a&#xff1b;ctrl-c将当前选择的字母复制到剪贴板&#xff1b;ctrl-x将当前选择的字母复制到剪贴板&#…

python 打包新方案

首先是打包一个最简单的python 代码使用 pyinstaller import os #直接读取文件获得python.exe 路径 # 待执行python路径 with open("path_run.txt","r",encoding"utf-8") as f:python_exe,pyf.readlines() os.system("{} {}".format(p…

关于Acunetix(AWVS)激活时候失败可能的方法

关于Acunetix(AWVS)激活的时候失败&#xff0c;可能的解决方法 如果自己的激活包大概率没有问题&#xff0c;但是自己却激活不了 可以在服务中关闭所有的进程&#xff0c;再进行文件替换 方法如下&#xff1a; 1.先卸载文件&#xff0c;卸载的干干净净 2.正常安装 3.安装完毕…

cuDNN 的初始设计

cuDNN V1.0在2014年的发布,并集成到 Caffe、Paddle 等深度学习框架中。论文 cuDNN: Efficient Primitives for Deep Learning 介绍了 NVIDIA 对于该库的设计和实现。近十年间&#xff0c;NVIDIA 迭代推出了8代架构&#xff0c;cuDNN 也更新到 8.9。硬件上引入了 Tensor Core&am…

超级详细的 VirtualBox 虚拟机安装 及入门教程

一、前言 虚拟机&#xff08;Virtual Machine&#xff09;指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。在实体计算机中能够完成的工作在虚拟机中都能够实现。 虚拟机是在一些开发测试工作中常常需要用到的功能&#xff0c;常见的虚拟机…

数学建模算法汇总(全网最全!含matlab案例代码)

数学建模常用的算法分类 全国大学生数学建模竞赛中&#xff0c;常见的算法模型有以下30种&#xff1a; 最小二乘法数值分析方法图论算法线性规划整数规划动态规划贪心算法分支定界法蒙特卡洛方法随机游走算法遗传算法粒子群算法神经网络算法人工智能算法模糊数学时间序列分析马…

【对比度增强】Learning Tone Curves for Local Image Enhancement(LTMNet)

文章目录 0. 前言1. 理解1.1 整体框架1.2 网络结构1.3 细节 2. 亮点3. 总结 0. 前言 LTMNet这篇文章借鉴了CLAHE算法&#xff0c;所有步骤与CLAHE一致&#xff0c;不同之处在于LTMNet中局部映射曲线是通过CNN预测得到&#xff0c;而CLAHE中是通过直方图均衡化而得。关于CLAHE&…

MySQL_第10章_创建和管理表

第10章_创建和管理表 讲师&#xff1a;尚硅谷 - 宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a; http://www.atguigu.com 1. 基础知识 1.1 一条数据存储的过程 存储数据是处理数据的第一步 。只有正确地把数据存储起来&#xff0c;我们才能…

【C++11】智能指针

目录 一、异常层层嵌套执行流乱跳容易导致内存泄漏 二、使用智能指针解决上述问题 1、RAII 2、像指针一样 3、智能指针RAII运算符重载 三、C98的auto_ptr 四、C11的unique_ptr和shared_ptr 1、unique_ptr唯一指针 2、shared_ptr共享指针 2.1shared_ptr是否线程安全 …

MySQL_第09章_子查询

第09章_子查询 讲师&#xff1a;尚硅谷 - 宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a; http://www.atguigu.com 子查询指一个查询语句嵌套在另一个查询语句内部的查询&#xff0c;这个特性从 MySQL 4.1 开始引入。 SQL 中子查询的使用大大…

计算机组成原理 作业5

作业5 题量: 21 满分: 100 作答时间:03-23 09:45至03-29 23:59 91.7分 一. 单选题&#xff08;共11题&#xff0c;35分&#xff09; 1. (单选题, 3分)计算机的存储器采用分级存储体系的主要目的是________。 A. 便于读写数据B. 便于系统…

NetSuite Sublist解释

今朝汇编一下Sublist主题的知识点以备忘。 2个数据源类型 Related Record - 以Saved Search建立的关联记录&#xff1b;Child Record - 父子表&#xff1b; 1. Related Record Saved Search关键点 这种形式的Sublist是利用Saved Search作为Sublist的数据源&#xff0c;将某…

【群智能算法】一种改进的白鲸优化算法IBWO【Matlab代码#17】

文章目录 1. 原始BWO算法1.1 勘探阶段1.2 开发阶段1.3 鲸落阶段 2. 改进白鲸优化算法2.1 Tent映射种群初始化2.2 反向学习策略 3. 部分代码展示4. 仿真结果展示5. 资源获取 1. 原始BWO算法 BWO算法的种群初始化和大多数智能算法相同&#xff0c;即随机产生搜索空间中的若干候选…