关于线程池的“惊群效应”_线程池的边际效应-程序员宅基地

技术标签: C++  操作系统  UNIX环境高级编程  

什么是惊群
        举一个很简单的例子,当你往一群鸽子中间扔一块食物,虽然最终只有一个鸽子抢到食物,但所有鸽子都会被惊动来争夺,没有抢到食物的鸽子只好回去继续睡觉, 等待下一块食物到来。这样,每扔一块食物,都会惊动所有的鸽子,即为惊群。对于操作系统来说,多个进程/线程在等待同一资源是,也会产生类似的效果,其结 果就是每当资源可用,所有的进程/线程都来竞争资源,造成的后果:
1)系统对用户进程/线程频繁的做无效的调度、上下文切换,系统系能大打折扣。
2)为了确保只有一个线程得到资源,用户必须对资源操作进行加锁保护,进一步加大了系统开销。

         最常见的例子就是对于socket描述符的accept操作,当多个用户进程/线程 监听在同一个端口上时,由于实际只可能accept一次,因此就会产生惊群现象,当然前面已经说过了,这个问题是一个古老的问题,但目前的内核版本已经修复了这个问题,一个链接过来,内核只会唤醒一个子进程出来accept,这样就不用担心惊群效应了。

但是对于线程池呢?

一个基本的线程池框架是基于生产者和消费者模型的。生产者往队列里面添加任务,而消费者从队列中取任务并进行执行。一般来说,消费时间比较长,一般有许多个消费者。当许多个消费者同时在等待任务队列的时候,也就发生了“惊群效应”。

// 线程池类型定义
struct thread_pool {
  int   max_threads;	  // 线程池中最大线程数限制
  int   curr_threads;	 // 当前线程池中总的线程数
  int   idle_threads;	 // 当前线程池中空闲的线程数
  pthread_mutex_t mutex;    // 线程互斥锁
  pthread_cond_t  cond;     // 线程条件变量
  thread_job *first;	  // 线程任务链表的表头
  thread_job *last;	   // 线程任务链表的表尾
  ...	
}

消费者一般在等待任务或者处理任务的过程中。

pthread_mutex_lock(&pool->mutex);
while(pool->first==NULL)//如果没有任务,一直等待
	pthread_cond_wait(&pool->cond, &pool->mutex);
pthread_mutex_unlock(&pool->mutex);
//回调处理
job->func(job->arg);

如果生产者有任务了,那么就通知消费者,生产者调用下面的代码,通知有任务了。
pthread_mutex_lock(&pool->mutex);
pthread_cond_signal(&pool->cond);
pthread_mutex_unlock(&pool->mutex);
注意上面的用法,不是pthread_cond_broadcast,这个是广播给所有等待任务的消费者,会产生惊群效应。
pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
但使用pthread_cond_signal不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是 根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则 根据等待时间的长短来确定哪个线程获得信号。但 无论如何一个pthread_cond_signal调用最多发信一次
有了上面这段话,我们就有理由相信,线程池并不会产生“惊群效应”。同时,这种方式使用多进程共享资源,等待管道或者其他资源等,提供cpu利用率。

如果有些系统设计比较简单,pthread_cond_signal还是会广播信号,那如何避免惊群呢?

方法一:要想避免如上线程池设计中的惊群问题,在 仍然共用一个线程互斥锁的条件下给每一个消费者线程创建一个线程条件变量,生产者线程在添加任务时, 找到空闲的消费者线程将任务置入该消费者的任务队列中,同时 只通知 (pthread_cond_signal) 该消费者的线程条件变量,消费者线程与生产者线程虽然共用相同的线程互斥锁(因为有全局资源及调用 pthread_cond_wait 所需),但 线程条件变量的通知过程却是定向通知的,未被通知的消费者线程不会被唤醒,这样惊群现象也就不会产生了。当然,还有一些设计上的细节需要注意,比如: 当没有空闲消费者线程时,需要将任务添加进线程池的全局任务队列中,消费者线程处理完自己的任务后需要查看一下线程池中的全局任务队列中是否还有未处理的任务。
【大意】:在线程池中,增加一组线程条件变量,对应于每一个线程。增加任务的时候,如果有空闲线程,那么只通知某一个空闲线程,并且将其置忙。忙与闲,可以通过条件变量来表征,用一个链表表示(类似连接池)。
如果所有线程都忙,那么就将任务加入全局队列,并且通知所有消费者(这时惊群是很小的,除非所有线程都刚好同一时刻完成任务,同一时刻争夺资源,否则只有极少数线程会发生惊群)。


推荐阅读有详细的介绍 《pthread_cond_signal与pthread_cond_wait》
【参考资料】


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wvtear/article/details/50434456

智能推荐

BASE64、MD5、SHA、HMAC几种加密算法-程序员宅基地

文章浏览阅读106次。BASE64编码算法不算是真正的加密算法。 MD5、SHA、HMAC这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法,我们称之为单向加密算法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。 BASE64 按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The ..._base 64编码 和mad5 和雪花算法

住宅IP、家庭宽带IP以及原生IP,它们有什么区别?谷歌开发者账号应选择哪种IP?-程序员宅基地

文章浏览阅读1.1k次。IP地址(Internet Protocol Address)是互联网协议地址的简称,是互联网通信的基础,互联网上每一个网络设备的唯一标识符每个在线的设备都需要一个IP地址,这样才能在网络中找到它们并进行数据交换。IP地址有很多种类型,今天跟大家简单分享一下住宅IP、家庭宽带IP以及原生IP的区别。住宅IP通常是指由互联网服务提供商(ISP)分配给家庭的或小型办公室使用的互联网连接IP地址,并可能随着网络连接的变化而变化。此类IP地址主要用于日常网络活动,如浏览网页、发送接收电子邮件、上网冲浪等。

如何更改layui form表单位置,宽度,颜色等_layui-form-item 宽度-程序员宅基地

文章浏览阅读2.6w次,点赞14次,收藏30次。如何更改layui form表单位置,宽度,颜色等_layui-form-item 宽度

【翻译】Efficient Data Loader for Fast Sampling-Based GNN Training on Large Graphs_pagraph: scaling gnn training on large graphs via -程序员宅基地

文章浏览阅读612次。写的非常好_pagraph: scaling gnn training on large graphs via computation-aware caching

炫酷的HTML代码-程序员宅基地

文章浏览阅读2.7w次,点赞61次,收藏285次。很炫酷的html代码:<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>star</title><script type="text/javascript">window.onload = function () {C = Math.cos; // cache Math objectsS = Math.si.._炫酷的html

【HDU - 1166】敌兵布阵 (线段树模板 单点更新+ 区间查询)-程序员宅基地

文章浏览阅读204次。题干:C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。 中央情报局要研究敌人究竟演习什...

随便推点

【免费题库】华为OD机试C卷 - 数字字符串组合倒序(Java 代码+解析)-程序员宅基地

文章浏览阅读2.3k次。题目描述对数字,字符,数字串,字符串,以及数字与字符串组合进行倒序排列。字符范围:由 a 到 z, A 到 Z,数字范围:由 0 到 9符号的定义:“-”作为连接符使用时作为字符串的一部分,例如“20-years”作为一个整体字符串呈现;连续出现 2 个 “-” 及以上时视为字符串间隔符,如“out--standing”中的”–“视为间隔符,是 2 个独立整体字符串”out”和”standing”;除了 1,2 里面定义的字符以外其他的所有字符,都是非法字符,作为字符串的间隔符处理,倒序后

Android(14) ArrayAdapter(数组适配器)的三种方法-程序员宅基地

文章浏览阅读5w次,点赞36次,收藏138次。ArrayAdapter数组适配器用于绑定格式单一的数据,数据源可以是集合或者数组列表视图(ListView)以垂直的形式列出需要显示的列表项。实现过程:新建适配器->添加数据源到适配器->视图加载适配器第一种:直接用ListView组件创建列表每一行只有一行文字效果如图:activity_list布局:<?xml version="1.0" e..._arrayadapter

助力商家健康经营 创业者为水滴直播点赞-程序员宅基地

文章浏览阅读43次。近日,水滴直播平台登上了舆论的风口浪尖。有人认为水滴直播涉嫌侵犯隐私,但也有人表示这种互联网新生事物可以有效规避很多风险,值得鼓励,不应一棒子打死。记者采访时发现,很多商家、创业者对于水滴直播纷纷表示支持,并直言水滴直播为他们的经营带来了很大帮助。 邹志泉在北京丰台区经营着一家批发厂家直销男女内衣裤的店铺,平时就打开水滴直播,分享他在店铺的经营画面。面对水滴直播涉及隐私的提问,邹志泉明确表...

java毕业设计宠物收养管理系统Mybatis+系统+数据库+调试部署-程序员宅基地

文章浏览阅读67次。springboot基于SpringBoot的电影社区网站。springboot基于springboot食品销售网站。ssm基于微信平台的校园汉服租赁系统的设计与实现。ssm基于SSM高校教师个人主页网站的设计与实现。ssm基于SSM框架的在线健康系统设计与实现。ssm基于HTML的武昌理工学院二手交易网站。ssm基于JavaEE的网上图书分享系统。ssm基于Javaee的项目任务跟踪系统。

Nginx使用之反向代理、负载均衡、动静分离教程。_php动静分离-程序员宅基地

文章浏览阅读61次。负载均衡是指将客户端的请求分发到多个后端服务器,以平衡服务器的负载。反向代理是指将客户端的请求转发到后端服务器,并将响应返回给客户端。通过配置反向代理,Nginx将转发所有来自客户端的请求到后端服务器,并将响应返回给客户端。通过这样的配置,Nginx将根据请求的URL路径选择是将请求转发到后端服务器还是直接返回静态资源文件。通过配置负载均衡,Nginx将按照指定的策略将客户端的请求分发到后端服务器上,从而实现负载均衡。配置反向代理:编辑Nginx配置文件(通常是nginx.conf),在。_php动静分离

HTML5有哪些新特性_谈谈html5的一些新特性-程序员宅基地

文章浏览阅读9.5k次,点赞3次,收藏18次。(一) 语义标签(二)增强型表单(三)视频和音频(四)Canvas绘图(五)SVG绘图(六)地理定位(七)拖放API(八) WebWorker(九) WebStorage(十)Web..._谈谈html5的一些新特性

推荐文章

热门文章

相关标签