【Linux】线程池

news2024/11/19 13:26:26

文章目录

      • 1.线程池概念
      • 2.线程池的优点
      • 3.线程池的应用场景
      • 4.线程池的实现
      • 5.STL和智能指针和线程安全
        • 5.1其他常见锁
        • 5.2读写锁

1.线程池概念

线程池是一种线程使用模式。

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

2.线程池的优点

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

可用线程的数量取决于可用的并发处理器、处理器内核、内存、网络、sockets等。

3.线程池的应用场景

(1).需要大量的线程来完成任务,且完成任务的时间比较短。

比如:WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大 。

(2). 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

(3).接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。

突发性大量客户请求,在没有线程池情况下,将产生大量线程,短时间内产生大量线程可能使内存到达极限。

4.线程池的实现

线程池对外暴露一个接口push接口,用于任务的加入。

图示:

image-20221123154359756

threadpool.hpp

实现线程模板:

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


const int Thread_num=10;
template <class T>
class threadpool{
private:
    threadpool(const int num=Thread_num):threadnum(num)
    {
        assert(threadnum>0);
        isrunning=false;
        pthread_cond_init(&cond_,nullptr);
        pthread_mutex_init(&mutex_,nullptr);
    }
    threadpool(const threadpool<T> &)=delete;    //拷贝构造
    threadpool<T>& operator=(const threadpool<T>&)=delete;
public:
    ~threadpool(){
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
    static threadpool<T>*getInstance(){
        static pthread_mutex_t mutex_static;
        if(instance==nullptr){
            //判断是否需要创建线程池
            pthread_mutex_lock(&mutex_static);
            if(instance==nullptr){
                instance=new threadpool<T>();
            }
            pthread_mutex_unlock(&mutex_static);
        }
        pthread_mutex_destroy(&mutex_static);
        return instance;
    }
    //类内函数默认第一个参数是this指针,而由于线程的执行函数只有一个参数,所以设置为静态函数,否则第一个参数被this占用
    static void* threadroutine(void* arg){
        pthread_detach(pthread_self()); //分离线程
        threadpool<T>* pool=static_cast<threadpool<T>* >(arg);
        prctl(PR_SET_NAME, "follower");
        //线程不断的获取任务,并执行
        while (true)
        {
            pool->lockQueue();
            //判断是否为空
            //为空则等待唤醒
            //不为空,取任务执行
            while (!pool->haveTask())
            {
                pool->waitForTask();
            }
            T t=pool->pop();
            int em1,em2;
            char op;
            t.get(&em1,&em2,&op);
            cout << "consumer[" << pthread_self() << "] " << (unsigned long)time(nullptr) 
            << " 消费了一个任务: " << em1 << op << em2 << "=" << t() << endl;
            pool->unlockQueue();
        }
        return nullptr;
    }
    void start(){
        assert(!isrunning);
        for(int i=0;i<threadnum;i++){
            pthread_t tid;
            pthread_create(&tid,nullptr,threadroutine,(void*)instance);
        }
    }

    void push(const T& t)
    {
        lockQueue();
        workqueue_.push(t);
        choiceThreadForHandler();
        unlockQueue();
    }
private:
    void lockQueue() { pthread_mutex_lock(&mutex_); }
    void unlockQueue() { pthread_mutex_unlock(&mutex_); }
    bool haveTask() { return !workqueue_.empty(); }
    void waitForTask() { pthread_cond_wait(&cond_, &mutex_); }
    void choiceThreadForHandler() { pthread_cond_signal(&cond_); }
    T pop()
    {
        T temp =workqueue_.front();
        workqueue_.pop();
        return temp;
    }
    queue<T> workqueue_;    //工作队列
    int threadnum;  //线程数量
    pthread_mutex_t mutex_; 
    pthread_cond_t cond_;
    bool isrunning; //判断线程池是否允许
    static threadpool<T>* instance;
};

template<class T>
threadpool<T>* threadpool<T>::instance=nullptr;

注意点:

  • 当某线程被唤醒时,其可能是被异常或是伪唤醒,或者是一些广播类的唤醒线程操作而导致所有线程被唤醒,使得在被唤醒的若干线程中,只有个别线程能拿到任务。此时应该让被唤醒的线程再次判断是否满足被唤醒条件,所以在判断任务队列是否为空时,应该使用while进行判断,而不是if。
  • pthread_cond_broadcast函数的作用是唤醒条件变量下的所有线程,而外部可能只Push了一个任务,我们却把全部在等待的线程都唤醒了,此时这些线程就都会去任务队列获取任务,但最终只有一个线程能得到任务。一瞬间唤醒大量的线程可能会导致系统震荡。这个现象也叫做惊群效应

为什么线程函数要设置为静态类型?

  • 类内函数默认第一个参数是this指针,而由于线程的执行函数只有一个参数,所以设置为静态函数,否则第一个参数被this占用
  • 静态成员函数属于类,而不属于某个对象,也就是说静态成员函数是没有隐藏的this指针的,因此我们需要将Routine设置为静态方法,此时Routine函数才真正只有一个参数类型为void*的参数。

测试文件

#include "threadpool.hpp"
#include "task.hpp"
#include <ctime>
#include <thread>

const std::string ops = "+-*/%";
int main()
{

    prctl(PR_SET_NAME, "main");
    //使用智能指针
    unique_ptr<threadpool<Task>>pool(threadpool<Task>::getInstance());
    pool->start();
    //生产任务
    srand((unsigned long)time(nullptr) ^ getpid() ^ pthread_self());
    while (true)
    {
        int em1=rand()%100,em2=rand()%30;
        char op=ops[rand()%4];
        Task t(em1,em2,op);
        pool->push(t);
        cout << "producter[" << pthread_self() << "] " << (unsigned long)time(nullptr) 
        << " 生产了一个任务: " << em1 << op << em2 << "=?" << endl;
        sleep(1);
    }
    return 0;
}

使用下面的指令对轻量级线程进行监控:

while :; ps -aL|grep -1&&ps -aL|grep threadpool_test|grep -v grep;echo "#######";sleep 1;done

执行结果:

image-20221123172208398

5.STL和智能指针和线程安全

STL中的容器不是线程安全的

  • STL 的设计初衷是将性能挖掘到极致, 而一旦涉及到加锁保证线程安全, 会对性能造成巨大的影响。
  • 而且对于不同的容器, 加锁方式的不同, 性能可能也不同(例如hash表的锁表和锁桶).
  • 因此 STL 默认不是线程安全. 如果需要在多线程环境下使用, 往往需要调用者自行保证线程安全

智能指针是否是线程安全的?

  • 对于 unique_ptr, 由于只是在当前代码块范围内生效, 因此不涉及线程安全问题.
  • 对于 shared_ptr, 多个对象需要共用一个引用计数变量, 所以会存在线程安全问题. 但是标准库实现的时候考虑到了这个问题, 基于原子操作(CAS)的方式保证 shared_ptr 能够高效, 原子的操作引用计数

5.1其他常见锁

  • 悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,
    行锁等),当其他线程想要访问数据时,被阻塞挂起。
  • 乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作。
  • CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试

自旋锁和挂起等待锁

  • 自旋锁:轮询检测锁是否锁就绪

挂起等待锁适合在临界区长时间允许占有锁的情况。而挂起等待锁适合在临界区运行时间短,等待锁时间短的情况。

自旋锁的接口

pthread_spin_init();
与互斥锁的接口一样,只需要将mutex修改为spin即可

5.2读写锁

在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读
的机会反而高的多。

通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。而读写锁就可以解决读多写少的情况。

image-20221123173603762

  • **注意:写独占,读共享,读锁优先级高 **

接口

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
/*
pref 共有 3 种选择
PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和
PTHREAD_RWLOCK_PREFER_READER_NP 一致
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
*/

初始化

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t
*restrict attr);

销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

**加锁和解锁 **

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

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

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

相关文章

土地覆盖数据集汇总

前言 土地覆盖数据是理解人类活动与全球变化之间复杂相互作用的关键信息来源,在改善生态系统、水文和大气模型的性能方面发挥着关键作用。而土地覆盖变化(LCC)是全球环境变化的起因和结果&#xff0c;影响着全球能量平衡和生物地球化学循环&#xff0c;进而影响气候变化和生态系…

Flutter中GetX系列二--Snackbar基本使用(顶部弹窗)

Snackbar基本使用 第一步&#xff1a;应用程序入口设置 当我们导入依赖后&#xff0c;在应用程序顶层把GetMaterialApp 作为顶层&#xff0c;如下所示 import package:flutter/material.dart; import package:get/get.dart;void main() {runApp(MyApp()); }class MyApp exte…

智慧物流解决方案-最新全套文件

智慧物流解决方案-最新全套文件一、建设背景行业痛点二、建设思路三、建设方案四、获取 - 智慧物流全套最新解决方案合集一、建设背景 随着物流国际化发展以及信息技术的普遍应用&#xff0c;物流管理由手工作业到半自动化、自动化&#xff0c;直至智能化的发展过程。面对现代…

MySQL之索引

引言 数据库作为项目中必不可少且运行速度相对较慢的一环&#xff0c;尤其是在大数据量下保证其更高的性能、更稳定的性能是每个后端程序员必备的技能。MySQL在执行查询语句时&#xff0c;会通过IO扫描磁盘&#xff0c;遍历数据表中的每一条数据&#xff0c;时间复杂度为O(N)&…

【机器学习】EM算法

EM算法 目录一、似然函数与极大似然估计二、Jenson不等式三、数学期望的相关定理四、边缘分布列五、EM算法一、似然函数与极大似然估计 例一 现有一个不透明的罐子&#xff0c;里面装有质地、大小均相同而颜色不同的黑白两种球&#xff08;数目未知&#xff09;。现要求在经过…

MyBatis 增删改查操作

什么是 MyBatis&#xff1f; mybatis 是一款优秀的持久层框架&#xff0c;用于简化 JDBC 开发 MyBatis 本是 Apach 的一个开源项目 iBatis&#xff0c;2021 年这个项目由 apach software foundation 迁移到了 google code&#xff0c;并且改名为 MyBatis。2013 年 11 月迁移到…

[附源码]java毕业设计氧气罐管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

js逆向之反调试之无限debugger解决

js逆向之反调试之无限debugger解决 文章目录 js逆向之反调试之无限debugger解决方案一方案二方案三方案一 右击debugger行数位置,点击add conditional breakpoint… 点击add conditional breakpoint.png 添加false,然后按回撤, 刷新网页,发现成功跳过无限debugger 修改成…

TiDB 6.0 新特性

TiDB 6.0 新特性 Placement Rules in SQL小表缓存内存悲观锁Top SQLTiDB Enterprise Manager(TiEM) Placement Rules in SQL Placement Rules in SQL 之前&#xff1a; 跨地域部署的集群&#xff0c;无法本地访问无法根据业务隔离资源&#xff0c;leader全在一个TiKV节点上…

CTFHub | UA注入

0x00 前言 CTFHub 专注网络安全、信息安全、白帽子技术的在线学习&#xff0c;实训平台。提供优质的赛事及学习服务&#xff0c;拥有完善的题目环境及配套 writeup &#xff0c;降低 CTF 学习入门门槛&#xff0c;快速帮助选手成长&#xff0c;跟随主流比赛潮流。 0x01 题目描述…

新发现,新挑战,技术出海的机遇与挑战丨PingCAP DevCon 2022 出海专场

现在报名活动&#xff0c;有机会获得限定好礼哦&#xff01;&#x1f446; 中国企业出海的格局和挑战正在发生重大改变。回到技术管理者熟悉的技术世界&#xff0c;过去两年的技术环境也发生了巨大的变化&#xff0c;开源软件与云服务的结合成为业界共识的潮流&#xff0c;在多…

linux篇【11】:linux下的线程<后序>

目录 一.线程互斥 1.三个概念 2.互斥 &#xff08;1&#xff09;在执行语句的任何地方&#xff0c;线程可能被切换走 &#xff08;3&#xff09;抢票场景中的问题 &#xff08;4&#xff09;解决方案 3.加锁 &#xff08;1&#xff09;加锁介绍 &#xff08;2&#xf…

C语言百日千题系列之《忘情水题》第一日

目录 绪论 1.最大数位置 2.与指定数字相同的数的个数 3.蓝桥杯2013年第四届真题-核桃的数量 4.求所给范围内水仙花数并排列 5.最大值和最小值的差 6.计算书费 7.角谷猜想 8. 最高的分数 9.年龄与疾病 10.-百钱百鸡问题 绪论 本文是C语言百日千题系列《忘情水题》的第…

BCN衍生物:endo-BCN-PEG4-TAMRA,endo-BCN-PEG4-Palmitic,endo-BCN-PEG4-DSPE的特点分享

凯新生物公司小编分享&#xff1a;endo-BCN-PEG4-TAMRA &#xff0c;endo-BCN-PEG4-Palmitic&#xff0c;endo-BCN-PEG4-DSPE这几种的物理相关数据。 1、endo-BCN-PEG4-TAMRA 四甲基罗丹明&#xff08;TAMRA&#xff09;-叠氮化物是一种化学探针&#xff0c;用于直接在活细胞中…

代码随想录算法训练营第八天|二叉树(截止到左叶子之和)

翻转二叉树 Leecode 226.翻转二叉树 链接&#xff1a;https://leetcode.cn/problems/invert-binary-tree/ 用递归来做&#xff0c;若是遇到空节点&#xff0c;直接return 然后交换左右节点&#xff0c;接着递归 class Solution { public:TreeNode* invertTree(TreeNode* r…

Java代码审计——SSH 框架审计技巧

目录 &#xff08;一&#xff09; SSH 框架简介 &#xff08;二&#xff09; Java SSH 框架审计技巧 &#xff08;一&#xff09; SSH 框架简介 上个月介绍了 SSM 框架&#xff0c;即 Spring MVC、Spring 和 MyBatis。接下来介绍 Java Web曾经开发的 SSH 框架&#xff0c;即 …

河北涿州水稻种植历史 国稻种芯·中国水稻节:保定效益双赢

河北涿州水稻种植历史 国稻种芯中国水稻节&#xff1a;保定效益双赢 央视网消息 保定日报讯&#xff08;通讯员张千 刘永兴 王蕾&#xff09;新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农…

浅谈前缀索引

一.什么是前缀索引 所谓前缀索引说白了就是对字符串或前n个字符建立索引 二.为什么选择前缀索引 一般来说使用前缀索引&#xff0c;可能都是因为整个字段的数据量太大&#xff0c;没有必要针对整个字段建立索引&#xff0c;前缀索引仅仅是选择一个字段的前n个字符作为索引&a…

Linux运维工程师的操作规范

&#xff0c;Linux运维工程师的操作规范从事运维有一段时间了&#xff0c;遇到过各式各样的问题&#xff0c;数据丢失&#xff0c;网站挂马&#xff0c;误删数据库文件&#xff0c;黑客攻击等各类问题。 今天简单整理一下&#xff0c;分享给各位小伙伴。 一、线上操作规范 1、…

你可见过如此细致的延时任务详解

概述 延时任务相信大家都不陌生&#xff0c;在现实的业务中应用场景可以说是比比皆是。例如订单下单 15 分钟未支付直接取消&#xff0c;外卖超时自动赔付等等。这些情况下&#xff0c;我们该怎么设计我们的服务的实现呢&#xff1f; 笨一点的方法自然是定时任务去数据库进行轮…