epoll多线程处理问题。

733 views
Skip to first unread message

杨光

unread,
Aug 15, 2008, 2:40:59 AM8/15/08
to dev4s...@googlegroups.com
EPOLL的处理函数只有几个,但怎么和多线程结合起来使用,找下资料,但没找到合适的。

for(;;) {
           nfds = epoll_wait(kdpfd, events, maxevents, -1);

           for(n = 0; n < nfds; ++n) {
               if(events[n].data.fd == listener) {
                   client = accept(listener, (struct sockaddr *) &local,
                                   &addrlen);
                   if(client < 0){
                       perror("accept");
                       continue;
                   }
                   setnonblocking(client);
                   ev.events = EPOLLIN | EPOLLET;
                   ev.data.fd = client;
                   if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
                       fprintf(stderr, "epoll set insertion error: fd=%d\n",
                               client);
                       return -1;
                   }
               }
               else
                   do_use_fd(events[n].data.fd);
           }
       }
----------------
上面这段代码是,LINUX下自带的列子。很多网上的,代码也都是用这个做入门教程。上面,这段代码,是把取SOCKET事物放在一个线程理做了。这样如果,很多个连接同时并发,这样肯定不行。在IOCP里,可以开多个工作线程,读取消息就OK。不知道EPOLL下要怎么做。是否能开多个线程同时 nfds = epoll_wait(kdpfd, events, maxevents, -1);,谁取到事物谁就处理。

Kasicass

unread,
Aug 19, 2008, 9:45:18 PM8/19/08
to dev4s...@googlegroups.com
杨光 写道:
> EPOLL的处理函数只有几个,但怎么和多线程结合起来使用,找下资料,但没找到
> 合适的。
>
> ...
> ----------------
> 上面这段代码是,LINUX下自带的列子。很多网上的,代码也都是用这个做入门教
> 程。上面,这段代码,是把取SOCKET事物放在一个线程理做了。这样如果,很多个
> 连接同时并发,这样肯定不行。在IOCP里,可以开多个工作线程,读取消息就OK。
> 不知道EPOLL下要怎么做。是否能开多个线程同时 nfds = epoll_wait(kdpfd,
> events, maxevents, -1);,谁取到事物谁就处理。

hi :-),

我说说我的理解吧,epoll() 本来就不是设计给多线程同时调用的,而是一个线程
可以同时处理多个事件源的数据(比如多个网络连接)。

对于多线程,应该是这样使用:
main thread:
n = epoll(...);
for (i = 0; i < n; i++)
new_thread(fds[i]);

收到事件时,开启一个线程去处理对应的 fd。


phoenix3

unread,
Aug 20, 2008, 4:51:34 AM8/20/08
to 高性能网络编程邮件列表

TO:Kasicass
我觉得你这样理解非常有道理。但是, new_thread(fds[i]);这个里面应该是新的RECV()线程吧。你能详细说下,这个线程的处理
吗?如果,CLIENT发来100个字节,而我的只收50个数据。其他50个数据希望其他new_thread(fds[i])接收。需要怎么做?这样
做的目的是,可能我的数据包就50字节,但客户端连续发来2个包,我希望其他线程接收。

关中刀客

unread,
Aug 20, 2008, 6:52:01 AM8/20/08
to 高性能网络编程邮件列表
我以前写的,不知道各位是何看法
http://guanzhongdaoke-gmail-com.javaeye.com/admin/blogs/189005

On 8月15日, 下午2时40分, "杨光" <yangguang2...@gmail.com> wrote:
> EPOLL的处理函数只有几个,但怎么和多线程结合起来使用,找下资料,但没找到合适的。
>
> for(;;) {
> nfds = epoll_wait(kdpfd, events, maxevents, -1);
>
> for(n = 0; n < nfds; ++n) {
> if(events[n].data.fd == listener) {
> client = accept(listener, (struct sockaddr *) &local,
> &addrlen);
> if(client < 0){
> perror("accept");
> continue;
> }
> setnonblocking(client);
> ev.events = EPOLLIN | EPOLLET;
> ev.data.fd = client;
> if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
> fprintf(stderr, "epoll set insertion error: fd=%d\n",
> client);
> return -1;
> }
> }
> else
> do_use_fd(events[n].data.fd);
> }
> }
> ----------------
> 上面这段代码是,LINUX下自带的列子。很多网上的,代码也都是用这个做入门教程。上面,这段代码,是把取SOCKET事物放在一个线程理做了。这样如果,很-多个连接同时并发,这样肯定不行。在IOCP里,可以开多个工作线程,读取消息就OK。不知道EPOLL下要怎么做。是否能开多个线程同时

flywav

unread,
Aug 28, 2008, 9:36:18 AM8/28/08
to 高性能网络编程邮件列表
对这个问题 非常感兴趣。我也研究了很久。

所谓的nginx 的单线程 5w 链接。 说白了 在事务处理非常简单的时候,应该很强 但是
逻辑处理一复杂, 必然会大大的拖累性能的。 而且单线程最大的问题是,多线程的性能没有发挥

其实很简单的例子就看出来了

epoll_waite() {

for (int x =0 ; x < 1000; x++){

do ()
}
}

如果do() 的时间 慢10倍 整个系统的速度就慢1000*10 倍。

这就意味着do的操作要快, 而如果有复杂的逻辑,肯定是不能实现的。


我的想法 有2种做法 第一个是 多产生一些epoll fd 让每个epoll 上面 跑一个线程,
第二 就是返回的结果由 多线程 以生产者 服务者形式工作, 那么 理想情况下
就是do() 这一个时间能返回了。但是我感觉难度大。












On 8月20日, 下午6时52分, 关中刀客 <guanzhongda...@gmail.com> wrote:
> 我以前写的,不知道各位是何看法http://guanzhongdaoke-gmail-com.javaeye.com/admin/blogs/189005

Tomt

unread,
Aug 28, 2008, 9:46:21 AM8/28/08
to dev4s...@googlegroups.com
我现在瞎整,弄个线程池,   epoll 后, 事件触发后,将任务注入到一个线程里面,不过水平太烂了,还在调试。 而且对  edge-triggered. 不了解。

Kasicass

unread,
Aug 31, 2008, 7:40:13 AM8/31/08
to dev4s...@googlegroups.com
phoenix3 写道:

> TO:Kasicass
> 我觉得你这样理解非常有道理。但是, new_thread(fds[i]);这个里面应该是新的RECV()线程吧。你能详细说下,这个线程的处理
> 吗?如果,CLIENT发来100个字节,而我的只收50个数据。其他50个数据希望其他new_thread(fds[i])接收。需要怎么做?这样
> 做的目的是,可能我的数据包就50字节,但客户端连续发来2个包,我希望其他线程接收。

hi :-),

对于你说的一个客户端发来两个 50 byte 的包,希望放到两个 thread 中处理,
我想,应该是下面这样的结构,才能做到并发处理:

main thread:
n = epoll(...);
for (i = 0; i < n; i++)

read_thread(fds[i]);

read thread:
data = read(fd);
put_into_queue(fd, data);

worker thread:
data = get_data_from_queue();
// do sth.

注解:通过几个 read_thread,将收到的完整的包放入 queue 中,然后几个 work
thread 不停处理 queue 中的数据。


不过上面仅仅是个人的猜想,其实我并没有做过 epoll + thread 的程序结构,我
觉得这类模型更适合 http request 这种两个 request 之间没有相互影响的行为。

而对于 MMO 这样逻辑复杂、交互很多的东西,一般都是:

n = epoll_wait()
for ( i = 0; i < n; i++ )
// do sth.

除了这里用 epoll,其他地方再优化下(比如数据异步存储),基本上单服 5k 应
该可以达到。

这里的处理方法,和后面 flywav 同学说的 nginx 的单线程 5w 链接是一样的。:-)

----
by kasicass

zhoubug

unread,
Aug 31, 2008, 10:28:12 AM8/31/08
to 高性能网络编程邮件列表


On 8月15日, 下午2时40分, "杨光" <yangguang2...@gmail.com> wrote:
你最后的这个问题在大多数平台可以回答说是可以的,因为apache最新的基于epoll 的mpm就是这样处理,而且它是多个进程这样调用。在对请求
的分配上也会是采用了工作线程池,也就是说是多个进程在同时epoll_wait,每个进程又对应自己的工作线程池,实现的很好,我最近抽取出这块的处
理框架有兴趣看下
http://code.google.com/p/zevent/

zhoubug

unread,
Aug 31, 2008, 10:45:01 AM8/31/08
to 高性能网络编程邮件列表
感觉你这个例子的设想不太合理,或者说可能交互设计有问题,不应该出现这种情况,否则线程切换太频繁拉。我考虑你或许在考虑一种一次数据接收不完,再次
接收的情况,
不管下次数据有谁处理,这个时候将句柄重新注册回event_set,下次触发时由epoll线程决定哪个特定线程或者任意一个工作线程处理这次事件更
合适.

Tomt

unread,
Aug 31, 2008, 10:56:08 AM8/31/08
to dev4s...@googlegroups.com
线程切换太频繁?
那用信号量挂起,是否能节省资源呢?

杨光

unread,
Aug 31, 2008, 11:01:44 PM8/31/08
to dev4s...@googlegroups.com
我这个程序测试了一段时间,最后用的模式如下:
ACCEPT()线程,一个,处理连接,接受到连接加入,EPOLL队列
EPOLL()线程,一个,wait_epoll()有数据过来,唤醒RECV()线程接收
RECV()线程,多个,这个线程负责接收数据,拼包,把完整的数据包放入处理队列。通过信号量阻塞;唤醒.

to:zhoubug 能否把下面这个代码发一份给我。
apache最新的基于epoll 的mpm




吴峥涛

unread,
Aug 31, 2008, 11:30:38 PM8/31/08
to dev4s...@googlegroups.com
 
新来报到,瞎扯两句。
 
我觉得epoll就是增强版的select,优势在于
1.  没有了FD_SIZE的句柄数限制
2.  每次epoll_wait获得的epoll set中都是有事件需要处理的句柄,不像select把所有句柄都返回了,降低了cpu消耗
 
在应用层来说epoll和select都是一样的,所以Reactor模式应该还是存在的,可以参考Doug lea的实现
http://www.javaworld.com.tw/jute/upload/2003/09/05/19890826.jpg
 
一般Acceptor一个线程,epoll/select一个线程,然后是一个线程池,这应该是一个典型的模式,具体使用起来效果还是不错的。尤其是Acceptor线程和epoll/select线程分开,在短链接频繁的情况下效果很好。

> Date: Sun, 31 Aug 2008 07:28:12 -0700
> Subject: Re: epoll多线程处理问题。
> From: china...@gmail.com
> To: dev4s...@googlegroups.com

吴峥涛

unread,
Aug 31, 2008, 11:39:08 PM8/31/08
to dev4s...@googlegroups.com

当然在多核的情况下,Acceptor/epoll线程数就不是1了,在mina中是cpu数+1,效果应该还不错,呵呵。

 

> </html

flywav

unread,
Sep 1, 2008, 5:00:31 AM9/1/08
to 高性能网络编程邮件列表
> n = epoll_wait()
> for ( i = 0; i < n; i++ )
> // do sth.

意味着 单线程在运行, 如果这个时候 这个对象和别的线程发生死锁,整个服务器就死掉了。

mmopg 应该很容易出现这样的锁争夺的事件吧。

Kasicass

unread,
Sep 3, 2008, 2:41:05 AM9/3/08
to dev4s...@googlegroups.com
hi flywav,

我想,如果程序锁争夺的事件很多,则 cpu 的利用率怎么都高不起来的。

flywav 写道:


>> n = epoll_wait()
>> for ( i = 0; i < n; i++ )
>> // do sth.
>
> 意味着 单线程在运行, 如果这个时候 这个对象和别的线程发生死锁,整个服务器就死掉了。
>
> mmopg 应该很容易出现这样的锁争夺的事件吧。
>

--
Best regards!

Kasicass/汤泽江 <kasicass_at_gmail_dot_com>

Reply all
Reply to author
Forward
0 new messages