[Linux]线程池

news2024/11/25 20:50:15

[Linux]线程池

文章目录

  • [Linux]线程池
    • 线程池的概念
    • 线程池的优点
    • 线程池的应用场景
    • 线程池的实现

线程池的概念

线程池是一种线程使用模式。线程池是一种特殊的生产消费模型,用户作为生产者,线程池作为消费者和缓冲区。

线程过多会带来调度开销,进而影响缓存局部和整体性能,而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。

线程池的优点

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

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

线程池的应用场景

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。

线程池的实现

下面我们实现一个简单的线程池,线程池中提供了一个任务队列,以及若干个线程(多线程)。

image-20231029145843142

  • 线程池中的多个线程负责从任务队列当中拿任务,并将拿到的任务进行处理。
  • 线程池对外提供一个Push接口,用于让外部线程能够将任务Push到任务队列当中。

线程池的代码如下:

#pragma once

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <vector>
#include <queue>

const int N = 5; // 线程池内线程数量

template <class T>
class ThreadPool
{
public:
    ThreadPool(int num = N) : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }

    void UnLockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }

    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    void threadWakeUP()
    {
        pthread_cond_signal(&_cond);
    }

    T getTask()
    {
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }

    bool isEmpty()
    {
        return _tasks.empty();
    }

    static void *threadRoutine(void *args)
    {
        pthread_detach(pthread_self());

        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        while (true)
        {
            tp->LockQueue();
            while (tp->isEmpty())
            {
                tp->threadWait();
            }
            T t = tp->getTask();
            tp->UnLockQueue();
            t.Run();//任务处理
        }
    }

    void Start()
    {
        pthread_t tid;
        for (int i = 0; i < _num; i++)
        {
            pthread_create(&tid, nullptr, threadRoutine, this);
        }
    }

    void PushTask(T &task) // 添加任务
    {
        LockQueue();
        _tasks.push(task);
        threadWakeUP();
        UnLockQueue();
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    int _num;                        // 线程数
    std::queue<T> _tasks;            // 任务队列

    pthread_mutex_t _mutex; // 保证互斥访问任务队列这一共享资源
    pthread_cond_t _cond;   // 根据任务队列中的任务数量控制线程的等待和运行
};

为什么线程池中需要有互斥锁和条件变量?

互斥锁: 任务队列是一个共享资源,外部线程可以调用添加任务的接口访问任务队列,线程池内部的线程可以直接访问任务队列处理任务,可能会造成任务队列的并发访问问题,因此需要利用互斥锁保护任务队列中的数据。

条件变量: 线程池当中的线程要从任务队列里拿任务,前提条件是任务队列中必须要有任务,因此线程池当中的线程在拿任务之前,需要先判断任务队列当中是否有任务,若此时任务队列为空,那么该线程应该进行等待,直到任务队列中有任务时再将其唤醒,因此我们需要引入条件变量。

当外部线程向任务队列中Push一个任务后,此时可能有线程正处于等待状态,因此在新增任务后需要唤醒在条件变量下等待的线程。

为什么线程池中的线程执行例程需要设置为静态方法?

使用pthread_create函数创建线程时,需要为创建的线程传入一个执行方法threadRoutine,该执行方法只有一个参数类型为void的参数,以及返回类型为void的返回值。

如果threadRoutine作为类的成员函数,该函数的第一个参数是隐藏的this指针,无法通过编译。而静态成员函数属于类,而不属于某个对象,也就是说静态成员函数是没有隐藏的this指针的,因此我们需要将threadRoutine设置为静态方法,此时threadRoutine函数才真正只有一个参数类型为void的参数。

但是在静态成员函数内部无法调用非静态成员函数,而我们需要在threadRoutine函数当中调用该类的某些非静态成员函数。因此我们需要在创建线程时,向threadRoutine函数传入的当前对象的this指针,此时我们就能够通过该this指针在threadRoutine函数内部调用非静态成员函数了。

任务类型的设计

由于线程池编写的是模板化的,因此任务类型可以是任意的,但是由于处理任务的逻辑是通过调用任务的Run函数,因此任务类中必须实现Run函数才能使用该线程池。

例如,实现一个计算任务类如下:

#include <cstdlib>
#include <iostream>

class Task
{
public:
    Task(int x, int y, char op) : _x(x), _y(y), _op(op), _result(0), _exitcode(0)
    {}

    void Run()//对传入数据进行操作
    {
        switch (_op)
        {
        case '+':
            _result = _x + _y;
            break;
        case '-':
            _result = _x - _y;
            break;
        case '*':
            _result = _x * _y;
            break;
        case '/':
            if (_y == 0) _exitcode = -1;
            else
                _result = _x / _y;
            break;
        case '%':
            if (_y == 0) _exitcode = -2;
            else
                _result = _x % _y;
            break;
        default:
            break;
        }
        std::string result = std::to_string(_x) + _op +  std::to_string(_y) + "=" + std::to_string(_result) + "(exicode:" + std::to_string(_exitcode);
        std::cout << result << std::endl;
    }

private:
    int _x;//左操作数
    int _y;//右操作数
    char _op;//操作符
    int _result;//算数结果
    int _exitcode;//退出码
};

线程池内的线程在从任务队列拿出任务进行处理的过程,并不需要关心这些任务的类型和来源,只需要拿到任务后执行对应的Run方法即可。

主线程实现

主线程只需要不断向任务队列当中Push任务就行了,此后线程池当中的线程会从任务队列当中获取到这些任务并进行处理。

#include "ThreadPoolv1.hpp"
#include "Task.hpp"
#include <memory>
#include <ctime>

using namespace std;

int main()
{
    std::unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>());
    tp->Start();
    time(nullptr);
    const char* ops = "+-*/%";
    while(true)
    {
        int x, y;
        x = rand() % 50;
        y = rand() % 50;
        char op = ops[rand()%5];
        Task t(x, y, op);
        tp->PushTask(t);
        sleep(1);
    }
    return 0;
}

运行代码后会产生六个线程,其中一个是主线程,另外五个是线程池内处理任务的线程:

image-20231029152724549

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

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

相关文章

【计算机视觉】对极几何

文章目录 一、极线约束&#xff08;Epipolar Constraint&#xff09;二、相机标定过的情况三、相机没有标定过的情况四、八点算法&#xff08;eight-point algorithm&#xff09; 我的《计算机视觉》系列参考UC Berkeley的CS180课程&#xff0c;PPT可以在课程主页看到。 在上一…

进行商城的测试用例设计思路是什么?

进行商城的测试用例设计时&#xff0c;可以考虑以下思路&#xff1a; 1. 功能测试&#xff1a;测试商城的基本功能是否正常工作&#xff0c;包括用户注册、登录、浏览商品、搜索商品、添加商品到购物车、下单、支付等。 2. 数据验证测试&#xff1a;验证商城中的数据是否正确…

深入浅出认识Kubernetes

用来管理容器&#xff0c;容器编排工具 容器化有助于打包软件来实现这些目标&#xff0c;从而使应用程序可以轻松快速地发布和更新&#xff0c;而无需停机。Kubernetes可帮助您确保那些容器化的应用程序在所需的位置和时间运行&#xff0c;并帮助他们找到工作所需的资源和工具。…

10阶杨辉三角

【任务需求】 定义一个函数&#xff0c;根据杨辉三角的数学概念&#xff0c;使用循环嵌套进行编写实现杨辉三角的关&#xff0c;并用for循环实现10阶杨辉三角&#xff0c;最后输出时需使10阶杨辉三角每行数字左右对称&#xff0c;按要求编写程序。 def triangle(rows):triang…

将数据文件,控制文件,日志文件分别放在不同的目录下,且数据库正常启动

一、定位数据文件、控制文件、日志文件的位置 注意&#xff1a;后序需要用到这些文件的位置&#xff0c;可以在查询完毕之后先截图保存 1.以管理员身份登录数据库 sqlplus / as sysdba2.查找数据文件位置 SELECT name FROM v$datafile;3.查找控制文件位置 SELECT name FROM …

设计模式:享元模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)

上一篇《原型模式》 下一篇《责任链模式》 简介&#xff1a; 享元模式&#xff0c;它是一种结构型设计模式&#xff0c;旨在有效地支持大量细粒度的对象共享&#xff0c;通过共享对象来减少内存消耗和…

【C++】缺省参数及函数重载

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1. 缺省参数1.1 缺省…

常见持久层框架赏析,到底是什么让你选择 MyBatis?

在绝大多数在线应用场景中&#xff0c;数据是存储在关系型数据库中的&#xff0c;当然&#xff0c;有特殊要求的场景中&#xff0c;我们也会将其他持久化存储&#xff08;如 ElasticSearch、HBase、MongoDB 等&#xff09;作为辅助存储。但不可否认的是&#xff0c;关系型数据库…

Ubuntu20.04操作系统安装及重中之重:系统分区

最近因为学习原因&#xff0c;需要将电脑设置为双系统&#xff0c;在windows10的系统下去安装Ubuntu操作系统。本来看网上相关的安装教程蛮多的&#xff0c;以为比较简单&#xff0c;结果一路过五关斩六将&#xff0c;坑的七零八落的&#xff0c;折腾了好久&#xff0c;才算安装…

【Java】LinkedList 集合

LinkedList集合特点 LinkedList 底层基于双向链表实现增删 效率非常高&#xff0c;查询效率非常低。 LinkedList源码解读分析 LinkedList 是双向链表实现的 ListLinkedList 是非线程安全的&#xff08;线程是不安全的&#xff09;LinkedList 元素允许为null,允许重复元素Linked…

基于 ARM+FPGA+AD的高精度数据采集系统设计

随着图像处理 、 工业控制 、 无线通信等领域的飞速发 展 &#xff0c; 对数据采集系统的速度 、 精度等性能要求也越来越高 。 这些要求都对数据采集系统的设计和实现提出了新的挑 战 。 目前数据采集系统的设计方案通常分为以下几类 &#xff1a; &#xff11; &#xff0…

【持续交付】个人网站

今天给大家演示下如何基于Vuepress尝试持续交付博客网站。 也尝试过其他的方案&#xff0c;比如使用Typora导出html文件&#xff0c;并scp该文件到服务器上。 效果图 该持续交付主流程如下图 提交代码后会触发webHook生成version.txt,部署脚本每分钟轮询一次检测是否存在vers…

私有云:【14】桌面映射外网

私有云&#xff1a;【14】桌面映射外网 1、选择服务器设置2、选中以下项&#xff0c;填写自己的公网IP3、最后这个也修改掉 1、选择服务器设置 2、选中以下项&#xff0c;填写自己的公网IP 3、最后这个也修改掉 桌面映射外网设置完成&#xff0c;前提要有公网IP噶&#xff0c;…

Docker 笔记(上篇)

Docker 概述 Docker 概念 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows操作系统的机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之…

【算法练习Day32】 斐波那契数爬楼梯使用最小花费爬楼梯

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 斐波那契数爬楼梯使用最小花…

所有电商API接口,淘宝API接口分类,1688API、拼多多API、京东API

前往接入API 淘宝API item_get 获取商品详情 根据商品ID查询商品标题价格描述等详情数据 淘宝API item_search 按关键字搜索商品 搜索关键字&#xff0c;显示商品总数&#xff0c;标题&#xff0c;图片&#xff0c;优惠价等数据 淘宝API item_fee 获取商品快递费用 输入商品…

基于RK3568高性价比全国产EMS储能解决方案(一)概述

储能产业链框架 储能产业链可分为上游“原材料及生产设备”、中游“储能系统”、下游“储能场景应用及后市场服务”。 图1 储能产业链框架图 产业链中游的“储能电池系统”主要包括“能量管理系统(EMS)”、“电池管理系统(BMS)”、“储能逆变器(PCS)”、“电池组”四个部分。…

Java创建一个长度为10的数组,利用Arrays.sort(), 为数组元素排序

程序要求&#xff1a;1&#xff09;创建一个整型数组&#xff0c;数组的长度为10. 2&#xff09;给数组元素赋值&#xff0c;要求乱序。 3&#xff09;利用fori循环将数组元素依次输出。 4&#xff09;利用Arrays.sort(), 为数组元素排序 5&#xff09;采用增加for循环将排…

shell语法大全(超级详细!!!!),非常适合入门

本文旨在对y总的Linux基础课shell语法做学习记录&#xff0c;指令较多&#xff0c;方便日后查找。 参考视频&#xff1a;Linux基础课 参考教程&#xff1a;Linux教程 1 概论 Linux中常见的shell脚本有很多种&#xff0c;常见的有&#xff1a; Bourne Shell(/usr/bin/sh或/bi…

Evade Deep Image Retrieval by Stashing Private Images in the Hash Space

摘要&#xff1a; 挑战&#xff1a; 当网络上的图像被大规模检索并被用作个人信息的丰富矿藏时&#xff0c;隐私也面临着风险&#xff1b; 攻击者可以通过从目标类别中查询类似图像以查找任何可用模型来提取私有图像。 提出&#xff1a; 提出了一种基于对抗性示例的新机制…