惊群问题

惊群问题

惊群问题是由于系统中有多个进程在等待同一个资源,当资源可用的时候,系统会唤醒所有或部分处于休眠状态的进程去争抢资源,但是最终只会有一个进程能够成功的响应请求并获得资源,但在这个过程中由于系统要对全部的进程唤醒,导致了需要对这些进程进行不必要的切换,从而会产生系统资源的浪费。

这种情况一般是accept或epoll_create在子进程中处于监听状态,也就是先创建子进程或者子线程然后调用accept或epoll_create阻塞监听的时候发生…,然而在先调用accept阻塞和先创建epoll_create,,然后在创建子进程或子线程也同样会产生惊群,这两种情况是有区别的。前者的epoll_create的惊群问题还没有解决,后者的惊群问题linux内核已经在2.6之后的版本周解决掉了。

 

1、在Linux系统中,多进程accept在比较旧的版本中存在惊群现象,在linux 2.6 版本之后就不存在这个问题了。在较老的unix系统版本中,每个阻塞在accept调用上的进程,当遇到连接的时候都会唤醒,从而产生惊群。

2、在linux系统中,如果fork在epoll_create()之后调用,也就是epoll_create在fork之前,会出现惊群现象,和accpet惊群现象类似,等待监听描述符的所有进程和线程都会被唤醒,Epoll后面的版本解决了这个问题,这种情况下所有的进程和线程共享同一个epoll_create之后的红黑树结构。

3、 当fork之后再调用epoll_create,也就是由于fork执行之后,每个子进程中复制了当前的这个监听sockfd,从而也会出现惊群现象,这个时候并不共享同一个红黑树,因为每个进程都自己维护一个红黑树。这种情况的惊群问题目前linux还没有解决,没解决的原因主要是linux系统不能确定是否在接受到响应信息的时候只需要唤醒一个进程,有可能需要唤醒多个进程,因而没有对这种情况进行处理。

4、Nginx服务器中处理这个问题的方式是使用互斥锁,在进行epoll_create之前,先让进程争抢这个互斥锁,抢到的进程就进行epoll_create没有抢到的就不会做任何反映,这样就解决了这个惊群问题。(具体细节还需要继续分析)Nginx中的epoll_create 应该在fork之后再创建的epoll_create,所以这种情况的惊群问题需要Nginx自己来处理。

Linux系统内核解决accept惊群问题的方法:

在Linux系统版本在2.6之前,所有监听同一个sockfd的进程都在一个等待队列上,当响应到来的时候会唤醒所有的等待进程。而其当时的解决方法是在调用accept之前先加一把锁,只有获得锁的进程才能accept成功,在Linux系统的2.6版本之后,引入了一个标记位,具体是什么还不清楚,但是引入这个标记位之后系统惊群就解决了。

补充:

线程池的惊群问题:(还不明白)

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

线程池惊群问题,其实质是由于当生产者线程向消费者线程发送消息来唤醒消费者线程的时候,由于有可能有多个消费者线程在监听,则会有可能唤醒多个线程,但是这种情况比较好解决:

在解决问题之前,需要先明白线程池的数据结构,以及生产者,消费者的处理逻辑:

线程池的数据结构是:

Struct pool

{

Int max;    //线程池中的最大线程个数

Int current;  //线程池中的当前线程个数

Int free;    //线程池中过的空闲线程个数

Pthread_mutex_tpt; //生产者,消费者互斥信号量

Prhread_cond_tcond; //生产者,消费者条件变量

Struct job*first;    //任务链表头

Struct job*last;    //任务链表尾

};

//消费者一般是处在等待唤醒去执行任务的状态中

Pthread_mutex_lock(&pool->pt)

While(pool->first == NULL)

{

Pthread_cond_wait(&pool->cond,&pool->pt);

}

Pthread_mutex_unlock(&pool->pt);

//任务处理

Job->func(job->args);

//生产者一般是处于生产任务,然后去通知消费者进行消费

Pthread_mutex_lock(&pool->pt);

Pthread_cond_signal(&pool->cond);

Pthread_mutex_unlock(&pool->pt);

注意上面的用法,不是pthread_cond_broadcast,这个是广播给所有等待任务的消费者,会产生惊群效应。

一般情况下,使用pthread_cond_signal 不会产生惊群问题。

pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。

但使用pthread_cond_signal不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。

有了上面这段话,我们就有理由相信,线程池并不会产生“惊群效应”。同时,这种方式使用多进程共享资源,等待管道或者其他资源等,提供cpu利用率。

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

简单理解就是给每个线程都设置一个条件变量,而不是只使用以上代码中的那一个条件变量。

在线程池中,增加一组线程条件变量,对应于每一个线程。增加任务的时候,如果有空闲线程,那么只通知某一个空闲线程,并且将其置忙。忙与闲,可以通过条件变量来表征,用一个链表表示(类似连接池)。

时间: 2024-10-13 22:03:42

惊群问题的相关文章

linux 惊群问题

1. 结论 对于惊群的资料,网上特别多,良莠不齐,也不全面.看的时候,有的资料说,惊群已经解决了,有的资料说,惊群还没解决.. 哪个才是对的?!  一怒之下,在研究各种公开资料的基础上,特意查对了linux源码,总结了此文.希望对有需要的人略有帮助,希望各位大神轻拍,如有错漏,不吝指教,感激不尽.([email protected]) 先说结论吧: 1. Linux多进程accept系统调用的惊群问题(注意,这里没有使用select.epoll等事件机制),在linux 2.6版本之前的版本存在

【转载】“惊群”,看看nginx是怎么解决它的

原文:http://blog.csdn.net/russell_tao/article/details/7204260 在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群.可以想见,效率很低下,许多进程被内核重新调度唤醒,同时去响应这一个事件,当然只有一个进程能处理事件成功,其他的进程在处理该事件失败后重新休眠(也有其他选择).这种性能浪费现象就是惊群. 惊群通常发

Redis 利用锁机制来防止缓存过期产生的惊群现象-转载自 http://my.oschina.net/u/1156660/blog/360552

首先,所谓的缓存过期引起的“惊群”现象是指,在大并发情况下,我们通常会用缓存来给数据库分压,但是会有这么一种情况发生,那就是在一定时间 内生成大量的缓存,然后当缓存到期之后又有大量的缓存失效,导致后端数据库的压力突然增大,这种现象就可以称为“缓存过期产生的惊群现象”! 以下代码的思路,就是利用“锁机制”来防止惊群现象.先看代码: class KomaRedis{ private $redis; //redis对象 private static $_instance = null; private

【转载】惊群问题的一些分析

Nginx学习之八-惊群问题 http://blog.csdn.net/xiajun07061225/article/details/9260535 “惊群”,看看nginx是怎么解决它的 http://russelltao.iteye.com/blog/1405352

Linux网络编程“惊群”问题总结

1.前言 我从事Linux系统下网络开发将近4年了,经常还是遇到一些问题,只是知其然而不知其所以然,有时候和其他人交流,搞得非常尴尬.如今计算机都是多核了,网络编程框架也逐步丰富多了,我所知道的有多进程.多线程.异步事件驱动常用的三种模型.最经典的模型就是Nginx中所用的Master-Worker多进程异步驱动模型.今天和大家一起讨论一下网络开发中遇到的“惊群”现象.之前只是听说过这个现象,网上查资料也了解了基本概念,在实际的工作中还真没有遇到过.今天周末,结合自己的理解和网上的资料,彻底将“

Nginx如何解决“惊群”现象

首先解释下什么是"惊群"现象:如果多个工作进程同时拥有某个监听套接口,那么一旦该套接口出现某客户端请求,此时就将引发所有拥有该套接口的工作进程去争抢这个请求,能争抢到的肯定只有某一个工作进程,而其他工作进程注定要无功而返,这种现象即为"惊群". Nginx解决这种"惊群"现象使用的是负载均衡的策略,接下来先结合Nginx的源码详细介绍下Nginx的这种负载均衡策略. 首先是Nginx如何开启负载均衡策略:当然运行的Nginx要是多进程模型,并且工

accept与epoll惊群 转载

今天打开 OneNote,发现里面躺着一篇很久以前写的笔记,现在将它贴出来. 1. 什么叫惊群现象 首先,我们看看维基百科对惊群的定义: The thundering herd problem occurs when a large number of processes waiting for an event are awoken when that event occurs, but only one process is able to proceed at a time. After

Nginx源码分析 - 主流程篇 - 多进程的惊群和进程负载均衡处理

Linux2.6版本之前还存在对于socket的accept的惊群现象.之后的版本已经解决掉了这个问题. 惊群是指多个进程/线程在等待同一资源时,每当资源可用,所有的进程/线程都来竞争资源的现象. Nginx采用的是多进程的模式.假设Linux系统是2.6版本以前,当有一个客户端要连到Nginx服务器上,Nginx的N个进程都会去监听socket的accept的,如果全部的N个进程都对这个客户端的socket连接进行了监听,就会造成资源的竞争甚至数据的错乱.我们要保证的是,一个链接在Nginx的

【Nginx】惊群问题

转自:江南烟雨 惊群问题的产生 在建立连接的时候,Nginx处于充分发挥多核CPU架构性能的考虑,使用了多个worker子进程监听相同端口的设计,这样多个子进程在accept建立新连接时会有争抢,这会带来著名的“惊群”问题,子进程数量越多越明显,这会造成系统性能的下降. 一般情况 下,有多少CPU核心就有配置多少个worker子进程.假设现在没有用户连入服务器,某一时刻恰好所有的子进程都休眠且等待新连接的系统调用(如 epoll_wait),这时有一个用户向服务器发起了连接,内核在收到TCP的S

linux惊群

基本概念:子进程继承父进程环境和上下文的大部分内容的拷贝,其中就包括文件描述符表. 父进程fork出来的子进程,复制父进程的文件描述符.这些文件描述符fd是独立的,但是文件描述符指向的系统文件表项是唯一的,即是 struct file本身唯一. 同理,fork得到的子进程和父进程共享同一个socket(套接字代表文件).fd与文件关联,通过绑定struct sockaddr套接字地址空间,跟特定的ip和端口绑定在一起. 所以在子进程中accept(listen,....),虽然listen在不同