关于惊群效应

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

对于惊群效应的场景描述,最常见的就是对于socket操作符的accept操作的描述。当多个用户进程/线程同时监听同一个端口时,由于实际上一个请求过来,只有一个进程/线程accept成功,所以就会产生惊群效应。

实际上这是一个很古老的问题。linux操作系统在内核层面很早就解决了这个问题,一个请求过来,内核只会唤醒一个进程来accept,这样就没有惊群现象了。但是在很多场景下,只要有竞争,就可能会出现惊群效应。比如常见的生产者-消费者模型,一般来说消费可能会比较耗时,所以消费者会有多个。当突然有生产者往队列里面投了一个job时,这些消费者就会一哄而上去队列中抢这个job,这就发生了惊群效应。

一个基本的线程池框架也是基于生产者-消费者模型的。也就是说只要用到了进程池或者线程池,你可能就避免不了要处理惊群效应带来的问题。所以你能感觉到惊群效应的无处不在了吗。。。

那么对于线程池这个模型,我们怎么解决它可能出现的惊群问题呢?

// 线程池类型定义
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;    // 线程任务链表的表尾
  ...   
}

一个线程池的定义一般如上所示:有最大线程数限制max_threads,当前线程数curr_threads和空闲线程数idle_threads,然后还有线程互斥锁,还有线程条件变量,每个线程条件变量对应一个唯一线程,然后还有一个线程任务队列(链表)。为什么需要线程条件变量和线程任务队列这些??

消费者一般在等待任务和处理任务的过程中,处理逻辑可以简化为:

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_signal,这个是根据进程条件变量来通知某一个等待消费的线程,而不是用pthread_cond_broadcast函数来广播给所有等待任务的消费者,这样就不会产生惊群效应。

pthread_cond_signal函数的作用上面也说了,是根据条件变量发送信号给一个正在休眠等待消费的线程,这个休眠消费线程获取到这个信号后马上激活,进行消费。

如果某一时刻,所有的消费线程都在进行各自的消费,此时又有一个生产者发送了一个任务,现在没有空闲消费者来接怎么办??

在这种情况下,生产者调用pthread_cond_signal函数也会立刻返回,只不过此时不是根据条件变量通知一个消费线程来accept,而是将此任务放到线程池队列中去,等待有空闲的消费线程过来取。

上面说了一个条件变量对应一个消费线程(意思就是每次都能根据当时的条件找到唯一的一个消费进程),那条件变量到底是什么呢??

条件变量的作用就是筛选出一个最合适的消费线程。那生产者生产了一个任务后,是怎么根据条件变量筛选出那个最合适的消费线程的呢??

假设有多个消费线程正在休眠,首先根据各消费线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。

在上面这种机制下,我们能保证生产者生产了一个任务后只会通知唯一的一个消费者线程,所以不会出现惊群效应。等等,到目前为止,咱们定义的线程池中,pthread_mutex好像并没有用到呢??

刚刚我们只是假设有pthread_cond_signal这种函数来保证每个任务只通知一个唯一的消费进程,那如果有的系统没有这样的单一通知机制,只有pthread_cond_broadcast函数这种广播机制怎么办??

此时pthread_mutex这个线程池互斥锁便派上用场了。在只有pthread_cond_broadcast函数这种广播机制的情况下,通过一个线程互斥锁,给每一个消费者线程创建一个条件变量......(好像原文后面说的不是很清楚)

【大意】:在线程池中,增加一组线程条件变量,对应于每一个线程。增加任务的时候,如果有空闲线程,那么只通知某一个空闲线程,并且将其置忙。忙与闲,可以通过条件变量来表征,用一个链表表示(类似连接池)。
如果所有线程都忙,那么就将任务加入全局队列,并且通知所有消费者(这时惊群是很小的,除非所有线程都刚好同一时刻完成任务,同一时刻争夺资源,否则只有极少数线程会发生惊群)。

上面也说道了linux在操作系统层已经避免了惊群效应的出现,但是nginx作为一个移植性非常高的web服务器,它自己也实现了一套避免出现惊群效应的机制。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • 转自:Youtherhttps://www.cnblogs.com/youtherhome/archive/201...
    njukay阅读 1,603评论 0 52
  • 引用自多线程编程指南应用程序里面多个线程的存在引发了多个执行线程安全访问资源的潜在问题。两个线程同时修改同一资源有...
    Mitchell阅读 1,973评论 1 7
  • 简介 线程创建 线程属性设置 线程参数传递 线程优先级 线程的数据处理 线程的分离状态 互斥锁 信号量 一 线程创...
    第八区阅读 8,547评论 1 6
  • 线程 在linux内核那一部分我们知道,线程其实就是一种特殊的进程,只是他们共享进程的文件和内存等资源,无论如何对...
    大雄good阅读 662评论 0 2
  • One saying like this, if you want to do something ,you mu...
    寄与已阅读 236评论 0 0