使用libevent多线程验证Linux上的服务器"惊群"现象
2014-10-19 21:27
113 查看
什么是惊群现象?
惊群(thundering herd)是指,只有一个子进程能获得连接,但所有N个子进程却都被唤醒了,这种情况将使性能受损。
举一个很简单的例子,当你往一群鸽子中间扔一块食物,虽然最终只有一个鸽子抢到食物,但所有鸽子都会被惊动来争夺,没有抢到食物的鸽子只好回去继续睡觉, 等待下一块食物到来。这样,每扔一块食物,都会惊动所有的鸽子,即为惊群。对于操作系统来说,多个进程/线程在等待同一资源时,也会产生类似的效果,其结 果就是每当资源可用,所有的进程/线程都来竞争资源,造成的后果:
1)系统对用户进程/线程频繁的做无效的调度、上下文切换,系统系能大打折扣。
2)为了确保只有一个线程得到资源,用户必须对资源操作进行加锁保护,进一步加大了系统开销。
最常见的例子就是对于socket描述符的accept操作,当多个用户进程/线程监听在同一个端口上时,由于实际只可能accept一次,因此就会产生惊群现象.这个问题是一个古老的问题,新的操作系统内核已经解决了这一问题。
在多线程情况下,每个线程都监听同一个fd,当有数据来的时候,是否会有惊群现象呢?验证如下
服务器端代码
客户端代码
测试效果图
参考文献
[1].http://blog.chinaunix.net/uid-26575352-id-3075103.html
[2].http://blog.csdn.net/nanjunxiao/article/details/9140769
[3].http://blog.163.com/leyni@126/blog/static/16223010220122611523786/
[4].http://simohayha.iteye.com/blog/658012
惊群(thundering herd)是指,只有一个子进程能获得连接,但所有N个子进程却都被唤醒了,这种情况将使性能受损。
举一个很简单的例子,当你往一群鸽子中间扔一块食物,虽然最终只有一个鸽子抢到食物,但所有鸽子都会被惊动来争夺,没有抢到食物的鸽子只好回去继续睡觉, 等待下一块食物到来。这样,每扔一块食物,都会惊动所有的鸽子,即为惊群。对于操作系统来说,多个进程/线程在等待同一资源时,也会产生类似的效果,其结 果就是每当资源可用,所有的进程/线程都来竞争资源,造成的后果:
1)系统对用户进程/线程频繁的做无效的调度、上下文切换,系统系能大打折扣。
2)为了确保只有一个线程得到资源,用户必须对资源操作进行加锁保护,进一步加大了系统开销。
最常见的例子就是对于socket描述符的accept操作,当多个用户进程/线程监听在同一个端口上时,由于实际只可能accept一次,因此就会产生惊群现象.这个问题是一个古老的问题,新的操作系统内核已经解决了这一问题。
在多线程情况下,每个线程都监听同一个fd,当有数据来的时候,是否会有惊群现象呢?验证如下
服务器端代码
//g++ -g libevent_server.cpp -o libevent_server -levent -lpthread //说明:服务器监听在本地19870端口, 等待udp client连接,有惊群现象: 当有数据到来时, 每个线程都被唤醒, 但是只有一个线程可以读到数据 // #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <event.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> using namespace std; int init_count = 0; pthread_mutex_t init_lock; pthread_cond_t init_cond; typedef struct { pthread_t thread_id; /* unique ID of this thread */ struct event_base *base; /* libevent handle this thread uses */ struct event notify_event; /* listen event for notify pipe */ } mythread; void *worker_libevent(void *arg) { mythread *p = (mythread *)arg; pthread_mutex_lock(&init_lock); init_count++; pthread_cond_signal(&init_cond); pthread_mutex_unlock(&init_lock); event_base_loop(p->base, 0); } int create_worker(void*(*func)(void *), void *arg) { mythread *p = (mythread *)arg; pthread_t tid; pthread_attr_t attr; pthread_attr_init(&attr); pthread_create(&tid, &attr, func, arg); p->thread_id = tid; pthread_attr_destroy(&attr); return 0; } void process(int fd, short which, void *arg) { mythread *p = (mythread *)arg; printf("I am in the thread: [%lu]\n", p->thread_id); char buffer[100]; memset(buffer, 0, 100); int ilen = read(fd, buffer, 100); printf("read num is: %d\n", ilen); printf("the buffer: %s\n", buffer); } //设置libevent事件回调 int setup_thread(mythread *p, int fd) { p->base = event_init(); event_set(&p->notify_event, fd, EV_READ|EV_PERSIST, process, p); event_base_set(p->base, &p->notify_event); event_add(&p->notify_event, 0); return 0; } int main() { struct sockaddr_in in; int fd; fd = socket(AF_INET, SOCK_DGRAM, 0); //在127.0.0.1:19870处监听 struct in_addr s; bzero(&in, sizeof(in)); in.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", (void *)&s); in.sin_addr.s_addr = s.s_addr; in.sin_port = htons(19870); bind(fd, (struct sockaddr*)&in, sizeof(in)); int threadnum = 10; //创建10个线程 int i; pthread_mutex_init(&init_lock, NULL); pthread_cond_init(&init_cond, NULL); mythread *g_thread; g_thread = (mythread *)malloc(sizeof(mythread)*10); for(i=0; i<threadnum; i++) { //10个线程都监听同一个socket描述符, 检查是否产生惊群现象? setup_thread(&g_thread[i], fd); } for(i=0; i<threadnum; i++) { create_worker(worker_libevent, &g_thread[i]); } //master线程等待worker线程池初始化完全 pthread_mutex_lock(&init_lock); while(init_count < threadnum) { pthread_cond_wait(&init_cond, &init_lock); } pthread_mutex_unlock(&init_lock); printf("IN THE MAIN LOOP\n"); while(1) { sleep(1); } //没有回收线程的代码 free(g_thread); return 0; }
客户端代码
//g++ -g libevent_client.cpp -o libevent_client // #include <iostream> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> using namespace std; int main() { struct sockaddr_in in; int fd; fd = socket(AF_INET, SOCK_DGRAM, 0); struct in_addr s; bzero(&in, sizeof(in)); in.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", (void *)&s); in.sin_addr.s_addr = s.s_addr; in.sin_port = htons(19870); string str = "I am Michael"; sendto(fd, str.c_str(), str.size(), 0, (struct sockaddr *)&in, sizeof(struct sockaddr_in)); return 0; }
测试效果图
参考文献
[1].http://blog.chinaunix.net/uid-26575352-id-3075103.html
[2].http://blog.csdn.net/nanjunxiao/article/details/9140769
[3].http://blog.163.com/leyni@126/blog/static/16223010220122611523786/
[4].http://simohayha.iteye.com/blog/658012
相关文章推荐
- "我佛山人"编写的表单验证类 Validator使用说明
- [转] "计算机无法访问,您可能没有权限使用网络资源.请与这台服务器的管理员联系以查明您是否有访问权限" 解决方法
- 使用eclipse 开发 嵌入式Linux驱动(注意事项)(make:***没有规则可言创建目标"all".停止 的解决方法)
- 无法启动链接服务器 "........" 的 OLE DB 访问接口 "SQLNCLI10" 的嵌套事务。由于 XACT_ABORT 选项已设置为 OFF,因此必须使用嵌套事务。
- linux 使用rpm安装软件时,遇到"warning: rpmts_HdrFromFdno: Header V3 RSA/SHA256 Signature, key ID fd431d51: NOK
- asp.net调试报错:IE8下验证您是服务器上"Debugger users"组的成员
- 关于网上邻居"此工作组的服务器列表当前无法使用"问题.
- 练手毛坯作品基于LINUX的"QQ闹眼子版本"(包括服务器与客户端<图形版与SHELL文字版>)
- 使用客户端身份验证方案“Anonymous”禁止 HTTP 请求"错误
- ASP.NET验证您是服务器上"Debugger users"组的成员
- matches 正则表达式的使用 编写代码,验证手机号码,并告知用户是"189""158""130" 号段各自的运营公司, 如果号码长度有误,告诉用户手机号码 不存在。
- linux环境下的"蚂蚁"-wget使用简介
- jsf项目使用shiro验证局部文件时出现"The server encountered an internal error..."
- c# asp.net验证您是服务器上"debugger users"组的成员
- Window访问Linux共享"您可能没有权限使用网络资源"问题解决
- linux无视passwd"无效的口令"提示强制使用简单密码
- "使用 Windows 身份验证"连接 SQL Server 失败
- 对不起,你的服务器没有安装动易组件(PE_Article6.dll),所以不能使用动易系统。"---解决方
- 由于端口"28553"正在使用,无法启动Visual Studio 开发服务器
- linux下"base64"命令工具的使用