Linux线程池

文章目录:

  • 线程池了解
  • 线程池模拟实现

线程池了解

线程池是一种常见的线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务,以避免在处理短时间任务时频繁地创建和销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度,提高程序性能。

线程池通常由以下组件组成:

  • 任务队列:用于存储待执行的任务。
  • 工作线程:线程池维护的多个线程,等待着监督管理者分配可并发执行的任务。
  • 监督管理者:负责管理线程池的工作线程,分配任务给空闲的线程执行,并监控工作线程的运行状态。

线程池的应用场景:

1️⃣ 需要大量的线程来完成任务,且完成任务的时间比较短。WEB 服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,可以想象一个热门网站的点击次数。但对于长时间的任务,比如一个 Telnet 连接请求,线程池的优点就不明显了。因为 Telnet 会话时间比线程的创建时间大多了。

2️⃣ 对性能要求苛刻的应用,比如要求服务器迅速相应客户请求。

3️⃣ 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将输出大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

4️⃣ 需要限制系统钟同时运行线程的数量,以避免系统资源被耗尽或因过多线程而导致系统性能能下降。

5️⃣ 需要异步执行任务,但又不想为每个任务创建一个新线程,因为这会消耗大量系统资源。

6️⃣ 需要在多个任务之间共享线程池之间共享线程池中的资源,例如共享数据库连接或共享线程安全的数据结构。

线程池的优点:

  • 线程池避免了在处理短时间任务时创建于销毁线程的代价。
  • 线程池不仅能够保证内核充分利用,还能防止过分调度。

注意:线程池中的可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络 sockets 等的数量。

线程池模拟实现

下面我们实现一个简单的线程池,线程池中提供一个任务队列,用于存储给出的任务,以及多个线程。

  1. 创建固定数量的线程池,循环从任务队列中获取任务对象。
  2. 获取到任务对象后,执行任务对象中的任务接口。

下面线程池中引入的任务 task.hpp 实现如下:

#pragma once

#include <iostream>
#include <string>

class Task
{
public:
    Task(int one = 0, int two = 0, char op = '0')
        : elemOne_(one), elemTwo_(two), operator_(op)
    {
    }

    int operator()() { return run(); }

    int run()
    {
        int result = 0;
        switch (operator_)
        {
        case '+':
            result = elemOne_ + elemTwo_;
            break;
        case '-':
            result = elemOne_ - elemTwo_;
            break;
        case '*':
            result = elemOne_ * elemTwo_;
            break;
        case '/':
            if (elemTwo_ == 0)
            {
                std::cout << "div zero,about " << std::endl;
                result = -1;
            }
            else
                result = elemOne_ / elemTwo_;
            break;
        case '%':
            if (elemTwo_ == 0)
            {
                std::cout << "mod zero,about " << std::endl;
                result = -1;
            }
            else
                result = elemOne_ % elemTwo_;
            break;
        default:
            std::cout << "非法操作:" << operator_ << std::endl;
            break;
        }
        return result;
    }

    int get(int *e1, int *e2, char *op)
    {
        *e1 = elemOne_;
        *e2 = elemTwo_;
        *op = operator_;
    }

private:
    int elemOne_;
    int elemTwo_;
    char operator_;
};

线程池代码实现如下:

#pragma once
#include <iostream>
#include <queue>
#include <unistd.h>
#include <cstdlib>
#include <memory>
#include <pthread.h>
#include <cassert>
#include <sys/prctl.h>
using namespace std;

int gThreadNum = 5; // 默认线程数

template <class T>
class ThreadPool
{
    // 加锁和解锁互斥锁
    void lockQueue() { pthread_mutex_lock(&mutex_); }
    void unlockQueue() { pthread_mutex_unlock(&mutex_); }
    // 检测队列是否为空
    bool isEmpty() { return taskQueue_.empty(); }
    // 等待任务队列中有新任务加入
    void waitForTask() { pthread_cond_wait(&cond_, &mutex_); }
    // 唤醒队列中的线程来处理任务
    void choiceThreadForHandler() { pthread_cond_signal(&cond_); }

public:
    // 构造函数,需要传入线程数
    ThreadPool(int threadNum = gThreadNum) : isStart_(false), threadNum_(threadNum)
    {
        assert(threadNum_ > 0);
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }

    // 析构函数,销毁互斥锁和条件变量
    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }

    // 线程执行函数,传入this指针
    static void *threadRoutine(void *args)
    {
        pthread_detach(pthread_self());                         // 线程分离
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args); // 获取线程池对象
        prctl(PR_SET_NAME, "follower");                         // 设置线程名
        while (1)
        {
            tp->lockQueue();      // 加锁
            while (tp->isEmpty()) // 如果任务队列为空,则等待
            {
                tp->waitForTask();
            }
            T t = tp->pop();   // 取出任务
            tp->unlockQueue(); // 解锁

            // for debug
            int one, two;
            char oper;
            t.get(&one, &two, &oper);
            // 执行任务并输出结果
            Log() << "完成计算任务:" << one << oper << two << "=" << t.run() << endl;
        }
    }

    // 从任务队列中取出任务
    T pop()
    {
        T temp = taskQueue_.front();
        taskQueue_.pop();
        return temp;
    }

    // 启动线程池
    void start()
    {
        assert(!isStart_); // 确保线程池未启动
        for (int i = 0; i < threadNum_; i++)
        {
            pthread_t temp;
            pthread_create(&temp, nullptr, threadRoutine, this);
        }
        isStart_ = true;
    }

    // 添加任务到任务队列中
    void push(const T &in)
    {
        lockQueue();
        taskQueue_.push(in);
        choiceThreadForHandler();
        unlockQueue();
    }
private:
    bool isStart_;          // 判断线程池是否启动
    int threadNum_;         // 线程数量
    queue<T> taskQueue_;    // 任务队列,存储待执行的任务
    pthread_mutex_t mutex_; // 互斥锁,保证线程安全
    pthread_cond_t cond_;   // 条件变量,用于线程同步
};

线程池实现说明:

  • 创建了一个模板类 ThreadPool ,该类中包含任务队列、互斥锁、条件变量等成员变量,以及相应的成员函数,用于添加任务、取出任务、启动线程池等操作。
  • 任务队列是被多个线程共享的临界资源,为了保证多个线程对任务队列的安全访问,需要引入互斥锁来保证同一时间只有一个线程可以对任务队列进行操作。
  • 线程池中的线程是通过不断地从任务队列中取出任务来进行处理的。当任务队列为空时,线程需要等待新的任务加入。为了实现该等待操作,需要引入条件变量来实现线程的同步。当任务队列为空时,线程将会等待条件变量,一旦有新的任务加入队列,就被通过条件变量被唤醒,从而继续执行任务。
  • 在线程执行函数中使用 while 替代 if 进行条件判断,当线程被唤醒后,应该再次检测条件是否满足。因为线程可能会出现伪唤醒的情况,即线程在没有明确被唤醒的情况下醒来。因此,这里使用 while 来检测条件是否满足。
  • 在获取任务后,应该尽快的释放锁,然后再处理任务,这样可以最大程度的减少临界区的持有时间,允许其它线程有机会执行并发操作,提高整体效率。
pthread_mutex_lock(&mutex);
// 获取任务
pthread_mutex_unlock(&mutex);
// 处理任务
  • 需要将线程例程(threadRoutine)设置为静态方法。这是因为 pthread_create 函数的第三个参数需要的是一个全局函数,它不能接受非静态成员函数。因为非静态成员函数默认第一个参数是 this ,而 pthread_create 创建的线程函数只能接受一个 void* 参数,这两者是不匹配的,因此,我们需要将其设置为静态的,然后显示地传递线程池对象作为参数。
    在这里插入图片描述

接下来对实现的线程池进行测试,测试代码如下:

#include "ThreadPool.hpp"
#include "task.hpp"
#include <ctime>

int main()
{
    prctl(PR_SET_NAME,"master");

    const string operators = "+-*/%";
    unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>()); 
    tp->start();

    srand((unsigned long)time(nullptr) ^ getpid() ^ pthread_self());
    // 派发任务的线程
    while (true)
    {
        int one = rand() % 50;
        int two = rand() % 10;
        char oper = operators[rand() % operators.size()];
        cout << "派发计算任务:" << one << oper << two << "=?" << endl;
        Task t(one, two, oper);
        tp->push(t);
        sleep(1);
    }
    return 0;
}

当程序运行起来之后,该进程中有一个 master 主线程,和5个 follower 子线程进行处理任务。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Word或者WPS批量调整文中图片大小的快捷方法

文章目录 0、前言1、编写宏代码2、在文档中调用宏实现一键批量调整3、就这么简单&#xff01; 0、前言 不知道大家是不是也和我一样&#xff0c;经常需要在编写的Word&#xff08;或者WPS&#xff09;文档里插入大量的图片&#xff0c;但是这些图片的尺寸大小一般都不一样&…

2D 3D 工业组态技术 meta2d JavaScript

本心、输入输出、结果 文章目录 2D 3D 工业组态技术 meta2d JavaScript前言2D 3D 工业组态技术 meta2d JavaScript 简介2D 3D 工业组态技术 meta2d JavaScript 特性丰富的组态能力0代码数据通信组态的应用多端适配能力强大的扩展能力追求卓越性能丰富的组件库资源广泛的应用场景…

致刘家窑中医院龚洪海医生:患者的感谢与敬意

你们好!我曾经是咱们这的一名患者&#xff0c;我叫李刚&#xff0c;今年45岁&#xff0c;不知道你们还有印象吗?我曾去过一些医院进行就诊&#xff0c;但都没有得到恰当的治疗&#xff0c;症状一直没有消失。得了这个病之后对我的生活以及工作打击都十分的大。经朋友介绍说刘家…

【Linux系统编程十七】:(基础IO4)--文件系统(inode与软硬链接)

【Linux系统编程十六】&#xff1a;文件系统&#xff08;inode与软硬链接&#xff09; 一.磁盘硬件二.文件系统(inode)三.软硬链接 一.磁盘硬件 Linux下的文件在磁盘中存储&#xff0c;文件的内容和属性是分开存储的&#xff01; 文件的内容存储在数据块。 文件的属性存储在in…

专业的软件第三方检测机构如何做性能测试?收费标准是多少?

随着软件信息技术的飞速发展&#xff0c;人们对于软件产品越来越依赖&#xff0c;从而用户对软件产品的稳定性和质量问题愈发看重。软件系统性能的好坏将严重影响该软件的质量和软件开发者的利益&#xff0c;为了更好的保障软件产品质量&#xff0c;软件企业会将性能测试交由软…

python 爬虫之urllib 库的相关模块的介绍以及应用

文章目录 urllib.request 模块打开 URL&#xff1a;发送 HTTP 请求&#xff1a;处理响应&#xff1a; 应用如何读取并显示网页内容提交网页参数使用HTTP 代理访问页面 urllib.request 模块 在 Python 中&#xff0c;urllib.request 模块是用于处理 URL 请求的标准库模块之一。…

vscode删除后重装还有原来的配置问题,彻底删除vscode,删除vscode安装过的插件和缓存

VSCode卸载后进行重新安装&#xff0c;发现新安装的还有原来的一些配置&#xff0c;卸载的不彻底&#xff0c;有时候也容易出问题&#xff0c;可按照如下方法卸载干净&#xff1a; 1.进入控制面板卸载VSCode&#xff0c;也可以在VSCode的安装目录下用程序自带的卸载程序 2.这…

灯光相机已就位!Cinerama LAND 销售活动开启序幕!

你准备好参加 The Sandbox 元宇宙中的重磅活动了吗&#xff1f;Cinerama LAND 拍卖即将来临&#xff0c;这是你踏入电影梦想世界的好机会。准备好构建你自己的沉浸式电影宇宙吧&#xff01;绝对不容错过&#xff01; 简要概括 &#x1f37f;活动开始日期&#xff1a;11 月 9 日…

如何在Jupyter Lab中安装不同的Kernel

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Django视图层()

视图层 django视图层&#xff1a;Django项目下的views.py文件&#xff0c;它的内部是一系列的函数或者是类,用来处理客户端的请求后处理并返回相应的数据 三板斧 HttpResponse # 返回字符串 render # 返回html页面&#xff0c;并且在返回浏览器之前还可以给html文件…

原生应用与hybrid app开发的流程区别

Hybrid App&#xff08;混合 App&#xff09;已经成为大家接触最为广泛的 App 形式&#xff0c;不管是我们用到的微信、支付宝还是淘宝、京东等大大小小的应用都非常热衷于Hybrid App 带来的研发效率提升和灵活性。 但我们正式进入到 hybrid App 的讨论前&#xff0c;有必要先…

自然语言处理(NLP)-spacy简介以及安装指南(语言库zh_core_web_sm)

spacy 简介 spacy 是 Python 自然语言处理软件包&#xff0c;可以对自然语言文本做词性分析、命名实体识别、依赖关系刻画&#xff0c;以及词嵌入向量的计算和可视化等。 1.安装 spacy 使用 “pip install spacy" 报错&#xff0c; 或者安装完 spacy&#xff0c;无法正…

springboot-error

Invalid bound statement (not found): com.example.demo.mapper.UserMapper.findAll 一直报错&#xff0c;找不到相应的mapper文件。 排除以下原因之后&#xff0c;还是不对&#xff1a; https://blog.csdn.net/xxpxxpoo8/article/details/127548543 最后发现是因为我的mapp…

【Transformer从零开始代码实现 pytoch版】(六)模型基本测试运行

模型基本测试及运行 &#xff08;1&#xff09;构建数据生成器 def data_generator(V, batch, num_batch):""" 用于随机生成copy任务的数据:param V: 随机生成数字的最大值1:param batch: 每次输送给模型更新一次参数的数据量:param num_batch: 输送多少次完成…

第四天课程 分布式搜索引擎1

分布式搜索引擎01 – elasticsearch基础 0.学习目标 1.初识elasticsearch 1.1.了解ES 1.1.1.elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 例如&#xff1a; …

第十九章总结:Java绘图

19.1&#xff1a;Java绘图类 19.2&#xff1a;绘制图形 package nineteentn; import java.awt.*; import javax.swing.*;public class DrawCircle extends JFrame {private final int OVAL_WIDTH 80; // 圆形的宽private final int OVAL_HEIGHT 80; // 圆形的高public DrawC…

nginx-静态资源实践(压缩配置,常见静态资源配置)

Nginx 实战搭建一个静态资源web服务器 第一个阶段访问单个文件 listen 80; #监听的端口 server_name localhost; #服务名称 #配置路径映射 location /geotools/ {alias geotools/; #将geotools/ 和/geotools/路径一一对应起来 }目录关系如下&#xff1a; nginx下面建一个g…

软考系统分析师知识点集锦一:企业信息化战略与实施

一、信息化战略体系(★★★★★) 1、信息资源规划ISP 信息资源规划是信息化建设的基础工程&#xff0c;是指对企业生产经营活动所需要的信息&#xff0c;对产生、获取、处理、存储、传输和利用等方面进行全面的规划。 IRP强调将需求分析与系统建模紧密结合起来&#xff0c;需…

酷柚易汛ERP - 其他入库单操作指南

1、应用场景 处理其他非采购类型的入库单据&#xff0c;比如赠品、获赔商品、以货抵债、借入、接受捐赠等不参与采购管理的入库类业务。 2、主要操作 2.1 新增其他入库单 打开【仓库】-【其他入库单】&#xff0c;选择商品后&#xff0c;根据存货核算方法自动计算出单位成本…

栈的实现---超详细

栈的实现 文章目录 栈的实现一、栈的模型二、栈的代码实现以及测试用例①栈的初始化②入栈③出栈④弹出栈顶⑤判断栈空间是否为空⑥计算栈空间长度⑦销毁栈⑧测试用例 一、栈的模型 首先栈有两个概念 1.数据结构里的栈。2.语言/操作系统中的栈(内存空间)&#xff0c;可能会在递…
最新文章