当前位置: 首页 > news >正文

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

1.线程池

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

① 主线程使用某种算法来主动选择子线程。最简单、最常用的算法是随机算法和 Round Robin(轮流选取)算法,但更优秀、更智能的算法将使任务在各个工作线程中更均匀地分配,从而减轻服务器 的整体压力。

② 主线程和所有子线程通过一个共享的工作队列来同步,子线程都睡眠在该工作队列上。当有新的任务到来时,主线程将任务添加到工作队列中。这将唤醒正在等待任务的子线程,不过只有一个子线程将获得新任务的”接管权“,它可以从工作队列中取出任务并执行之,而其他子线程将继续睡眠在工作队列上。

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

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

1.1 线程池类定义

// 线程池类,将它定义为模板类是为了代码复用,模板参数T是任务类
template<typename T>
class threadpool {
public:/*thread_number是线程池中线程的数量,max_requests是请求队列中最多允许的、等待处理的请求的数量*/threadpool(int thread_number = 8, int max_requests = 10000);~threadpool();bool append(T* request);private:/*工作线程运行的函数,它不断从工作队列中取出任务并执行之*/static void* worker(void* arg);void run();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;                    
};

1.2 线程池创建与回收

template< typename T >
threadpool< T >::threadpool(int thread_number, int max_requests) : m_thread_number(thread_number), m_max_requests(max_requests), m_stop(false), m_threads(NULL) {if((thread_number <= 0) || (max_requests <= 0) ) {throw std::exception();}// 线程id初始化m_threads = new pthread_t[m_thread_number];if(!m_threads) {throw std::exception();}// 创建thread_number 个线程,并将他们设置为脱离线程。for ( int i = 0; i < thread_number; ++i ) {printf( "create the %dth thread\n", i);// 循环创建线程,并将工作线程按要求进行运行if(pthread_create(m_threads + i, NULL, worker, this ) != 0) {delete [] m_threads;throw std::exception();}// 将线程进行分离后,不用单独对工作线程进行回收if( pthread_detach( m_threads[i] ) ) {delete [] m_threads;throw std::exception();}}
}template< typename T >
threadpool< T >::~threadpool() {delete [] m_threads;m_stop = true;
}

1.3 向请求队列中添加任务

通过list容器创建请求队列,向队列中添加任务时,通知互斥锁保证线程安全,添加完成后通过信号量提醒有任务要处理,最后注意线程同步。

template< typename T >
bool threadpool< T >::append( T* request )
{// 操作工作队列时一定要加锁,因为它被所有线程共享。m_queuelocker.lock();// 根据硬件,预先设置请求队列的最大值if ( m_workqueue.size() > m_max_requests ) {m_queuelocker.unlock();return false;}// 添加任务m_workqueue.push_back(request);m_queuelocker.unlock();// 信号量提醒有任务要处理m_queuestat.post();return true;
}

1.4 线程处理函数

内部访问私有成员函数run,完成线程处理

template< typename T >
void* threadpool< T >::worker( void* arg )
{// 将参数强转为线程池类,调用成员方法threadpool* pool = ( threadpool* )arg;pool->run();return pool;
}

1.5 run执行任务

主要实现,工作线程从请求队列中取出某个任务进行处理,注意线程同步 

template< typename T >
void threadpool< T >::run() {while (!m_stop) {// 信号量等待m_queuestat.wait();// 被唤醒后先加互斥锁m_queuelocker.lock();if ( m_workqueue.empty() ) {m_queuelocker.unlock();continue;}// 从请求队列中取出第一个任务T* request = m_workqueue.front();m_workqueue.pop_front();m_queuelocker.unlock();if ( !request ) {continue;}// process(模板类中的方法,这里是http类)进行处理request->process();}}

1.6 注意事项【总结来源两猿社的最新版Web服务器项目详解 - 03 半同步半反应堆线程池(下)】

pthread_create的函数原型

#include <pthread.h>
int pthread_create (pthread_t *thread_tid,//返回新生成的线程的idconst pthread_attr_t *attr,//指向线程属性的指针,通常设置为NULLvoid * (*start_routine) (void *),//处理线程函数的地址void *arg);//start_routine()中的参数

在 pthread_create函数原型中的第三个参数,为函数指针,指向处理线程函数的地址。且该函数为静态函数,所以在处理线程函数为类成员函数时,需要将其设置为静态成员函数。

// 线程池类,将它定义为模板类是为了代码复用,模板参数T是任务类
template<typename T>
class threadpool {
private:/*工作线程运行的函数,它不断从工作队列中取出任务并执行之*/static void* worker(void* arg);
public:...
};

【原因】

  • pthread_create的函数原型中的第三个参数的类型为函数指针,指向的线程处理函数参数类型为(void *)。
  • 线程函数若为类成员函数,则this指针会作为默认参数被传进函数中,无法和线程函数参数(void *)匹配,无法编译。
  • 由于静态成员函数里面没有this指针,能解决线程函数参数(void*)匹配问题。

完整代码:

threadpool.h

#ifndef THREADPOOL_H
#define THREADPOOL_H#include <list>
#include <cstdio>
#include <exception>
#include <pthread.h>
#include "locker.h"// 线程池类,将它定义为模板类是为了代码复用,模板参数T是任务类
template<typename T>
class threadpool {
public:/*thread_number是线程池中线程的数量,max_requests是请求队列中最多允许的、等待处理的请求的数量*/threadpool(int thread_number = 8, int max_requests = 10000);~threadpool();bool append(T* request);private:/*工作线程运行的函数,它不断从工作队列中取出任务并执行之*/static void* worker(void* arg);void run();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;                    
};template< typename T >
threadpool< T >::threadpool(int thread_number, int max_requests) : m_thread_number(thread_number), m_max_requests(max_requests), m_stop(false), m_threads(NULL) {if((thread_number <= 0) || (max_requests <= 0) ) {throw std::exception();}m_threads = new pthread_t[m_thread_number];if(!m_threads) {throw std::exception();}// 创建thread_number 个线程,并将他们设置为脱离线程。for ( int i = 0; i < thread_number; ++i ) {printf( "create the %dth thread\n", i);if(pthread_create(m_threads + i, NULL, worker, this ) != 0) {delete [] m_threads;throw std::exception();}if( pthread_detach( m_threads[i] ) ) {delete [] m_threads;throw std::exception();}}
}template< typename T >
threadpool< T >::~threadpool() {delete [] m_threads;m_stop = true;
}template< typename T >
bool threadpool< T >::append( T* request )
{// 操作工作队列时一定要加锁,因为它被所有线程共享。m_queuelocker.lock();if ( m_workqueue.size() > m_max_requests ) {m_queuelocker.unlock();return false;}m_workqueue.push_back(request);m_queuelocker.unlock();m_queuestat.post();return true;
}template< typename T >
void* threadpool< T >::worker( void* arg )
{threadpool* pool = ( threadpool* )arg;pool->run();return pool;
}template< typename T >
void threadpool< T >::run() {while (!m_stop) {m_queuestat.wait();m_queuelocker.lock();if ( m_workqueue.empty() ) {m_queuelocker.unlock();continue;}T* request = m_workqueue.front();m_workqueue.pop_front();m_queuelocker.unlock();if ( !request ) {continue;}request->process();}}#endif

locker.h

​
#ifndef LOCKER_H
#define LOCKER_H#include <exception>
#include <pthread.h>
#include <semaphore.h>// 线程同步机制封装类// 互斥锁类
class locker {
public:locker() {if(pthread_mutex_init(&m_mutex, NULL) != 0) {throw std::exception();}}~locker() {pthread_mutex_destroy(&m_mutex);}bool lock() {return pthread_mutex_lock(&m_mutex) == 0;}bool unlock() {return pthread_mutex_unlock(&m_mutex) == 0;}pthread_mutex_t *get(){return &m_mutex;}private:pthread_mutex_t m_mutex;
};// 条件变量类
class cond {
public:cond(){if (pthread_cond_init(&m_cond, NULL) != 0) {throw std::exception();}}~cond() {pthread_cond_destroy(&m_cond);}bool wait(pthread_mutex_t *m_mutex) {int ret = 0;ret = pthread_cond_wait(&m_cond, m_mutex);return ret == 0;}bool timewait(pthread_mutex_t *m_mutex, struct timespec t) {int ret = 0;ret = pthread_cond_timedwait(&m_cond, m_mutex, &t);return ret == 0;}bool signal() {return pthread_cond_signal(&m_cond) == 0;}bool broadcast() {return pthread_cond_broadcast(&m_cond) == 0;}private:pthread_cond_t m_cond;
};// 信号量类
class sem {
public:sem() {if( sem_init( &m_sem, 0, 0 ) != 0 ) {throw std::exception();}}sem(int num) {if( sem_init( &m_sem, 0, num ) != 0 ) {throw std::exception();}}~sem() {sem_destroy( &m_sem );}// 等待信号量bool wait() {return sem_wait( &m_sem ) == 0;}// 增加信号量bool post() {return sem_post( &m_sem ) == 0;}
private:sem_t m_sem;
};#endif​

相关文章:

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

1.线程池 线程池是由服务器预先创建的一组子线程&#xff0c;线程池中的线程数量应该和 CPU 数量差不多。线程池中的所有子线程都运行着相同的代码。当有新的任务到来时&#xff0c;主线程将通过某种方式选择线程池中的某一个子线程来为之服务。相比与动态的创建子线程&#x…...

Linux中的用户、组和权限

一&#xff0c;Linux的安全模型 1.安全3A Authentication(认证),Authorization(授权),Accounting(审计)(AAA)是用于对计算机资源的访问、策略执行、审计使用情况和提供服务账单所需信息等功能进行智能控制的基本组件的一个术语。大多数人认为这三个组合的过程对有效的网络管理和…...

python学习--基本数据类型之字典

python中数据类型 第一类&#xff1a;不可变类型、静态数据类型、不支持增删改操作 数字&#xff08;number&#xff09;字符串&#xff08;string&#xff09;元组&#xff08;tuple&#xff09; 第二类&#xff1a;可变类型、动态数据类型、支持增删改操作 列表&#xff…...

【OpenCV入门】第九部分——模板匹配

文章结构 模板匹配方法单模板匹配单目标匹配多目标匹配 多模板匹配 模板匹配方法 模板是被查找的图像。模板匹配是指查找模板在原始图像中的哪个位置的过程。 result cv2.matchTemplate(image, templ, method, mask)image&#xff1a; 原始图像templ&#xff1a; 模板图像&a…...

在设计web页面时,为移动端设计一套页面,PC端设计一套页面,并且能自动根据设备类型来选择是用移动端的页面还是PC端的页面。

响应式设计&#xff0c;即移动端和PC端共用一个HTML模式&#xff0c;网站的程序和模板自动根据设备类型和屏幕大小进行自适应调整。这种方法我不喜欢&#xff0c;原因是不能很好保证各种客户端的效果&#xff0c;里面存在各种复杂的兼容性等问题。 我喜欢为不同的客户端写不同的…...

微信小程序地图应用总结版

1.应用场景&#xff1a;展示公司位置&#xff0c;并打开第三方app&#xff08;高德&#xff0c;腾讯&#xff09;导航到目标位置。 &#xff08;1&#xff09;展示位置地图 uniapp官网提供了相关组件&#xff0c;uniapp-map组件https://uniapp.dcloud.net.cn/component/map.ht…...

分支创建查看切换

1、初始化git目录&#xff0c;创建文件并将其推送到本地库 git init echo "123" > hello.txt git add hello.txt git commit -m "first commit" hello.txt$ git init Initialized empty Git repository in D:/Git/git-demo/.git/ AdministratorDESKT…...

参编三大金融国标,奇富科技以技术促行业规范化演进

近期&#xff0c;由中国互联网金融协会领导制定的《互联网金融智能风险防控技术要求》《互联网金融个人网络消费信贷信息披露》《互联网金融个人身份识别技术要求》三项国家标准颁布&#xff0c;由国家市场监督管理总局、国家标准化管理委员会发布&#xff0c;奇富科技作为核心…...

芯片开发之难如何破解?龙智诚邀您前往DR IP-SoC China 2023 Day

2023年9月6日&#xff08;周三&#xff09;&#xff0c;龙智即将亮相D&R IP-SoC China 2023 Day&#xff0c;呈现集成了Perforce与Atlassian产品的芯片开发解决方案&#xff0c;助力企业更好、更快地进行芯片开发。 龙智资深顾问、技术支持部门负责人李培将带来主题演讲—…...

Gof23设计模式之策略模式

1.概述 该模式定义了一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以相互替换&#xff0c;且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式&#xff0c;它通过对算法进行封装&#xff0c;把使用算法的责任和算法的实现分割开来&#xff0c;…...

SAP 物料主数据屏幕增强

增强步骤 1.为主表添加一个附加结构 根据业务需求新建一个结构&#xff0c;结构中放入需要增强的屏幕字段并激活。 打开事务代码SE11&#xff0c;在需要保存的主表中添加这个附加结构并激活。 注&#xff1a;根据业务需求及屏幕增强的视图判断需要保存的主表是哪张&#xff…...

数据库-索引

介绍&#xff1a; 索引是帮助数据库高效获取数据的数据结构 优缺点&#xff1a; 优点&#xff1a;提高数据查询的效率&#xff0c;降低数据库的IO成本 通过索引列对数据进行排序&#xff0c;降低数据排序的成本&#xff0c;降低cpu消耗 缺点&#xff1a;索引会占用存储空间 索…...

Excel·VBA二维数组组合函数的应用实例

看到一个问题《关于#穷举#的问题&#xff0c;如何解决&#xff1f;(语言-开发语言)》&#xff0c;对同一个数据存在“是/否”2种状态&#xff0c;判断其是否参与计算&#xff0c;并输出一系列数据的“是/否”状态的结果 目录 方法1&#xff1a;二维数组组合函数结果 方法2&am…...

hive anti join 的几种写法

t_a 表的记录如下 c1 | :———— | a | b | c | 生成 SQL 如下&#xff1a; create table t_a(c1 string); insert into t_a values("a"),("b"),("c");t_b 表的记录如下 c1bm 生成 SQL 如下&#xff1a; create table t_b(c1 string); in…...

使用Android原生制作毛玻璃效果图片

毛玻璃效果&#xff0c;也被称为模糊效果&#xff0c;是许多现代应用中流行的一种视觉效果。在 Android 中&#xff0c;我们可以通过多种方式实现该效果。本文将探讨如何使用 Android 原生的 Bitmap 类和 RenderScript 来实现毛玻璃效果。 1. 准备工作 首先&#xff0c;你需要…...

软件设计的七大原则

一. 软件设计的七大原则 单一职责原则&#xff1a;一个类只负责一个功能领域中的饿相应职责。开闭原则&#xff1a;对扩展开放&#xff0c;对修改关闭&#xff0c;多使用抽象类和接口&#xff0c;应该尽量使这个系统能够扩展新的功能&#xff0c;通过扩展来实现变化&#xff0…...

Windows下安装配置Nginx

nginx安装 官网下载地址 https://nginx.org/en/download.html 推荐使用稳定版本 截止时间2023年9月5日稳定版本为 1.24.0 百度网盘 链接&#xff1a;https://pan.baidu.com/s/1cXm-jN2fMzKdVMRhbG72Fg 提取码&#xff1a;9hcq 下载完成以后,得到nginx压缩包; 双击启动nginx.…...

数据结构类型

1.在C和C中static关键字的用法 在C中 1.static修饰未初始化全局变量&#xff0c;默认结果为0 2.static修饰局部变量&#xff0c;延长生命周期&#xff0c;生命周期不是作用域&#xff0c;它依旧是局部变量 3.static修饰函数只能在当前文件中调用&#xff0c;不可用跨文件调用…...

WPF元素绑定

简单的说&#xff0c;数据绑定是一种关系&#xff0c;该关系告诉WPF从源对象提取一些信息&#xff0c;并用这些信息设置目标对象的属性。目标属性始终是依赖属性&#xff0c;通常位于WPF元素中——毕竟&#xff0c;WPF数据绑定的最终目标是在用户界面中显示一些信息。然而&…...

centos编译升级cmake,痛苦的Linux小白

环境 root 用户 下载 cmake官网下载地址&#xff1a;https://cmake.org/download/ 获取下载地址&#xff0c;右击cmake-3.27.4.tar.gz 命令行输入链接地址&#xff0c;下载 wget https://github.com/Kitware/CMake/releases/download/v3.27.4/cmake-3.27.4.tar.gz解压 tar -zx…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...