我正在尝试开发一个服务,其中包含许多客户端和服务器套接字(服务器服务以及连接到托管组件并保持不变的客户端),这些服务器是同步轮询的IO::Select
.我们的想法是处理I/O和/或请求通过工作线程池产生的处理需求.
shared
使数据在Perl(threads::shared
)中的线程之间可共享的关键字有其限制 - 句柄引用不属于可共享的基元.
在我发现无法共享句柄和/或句柄引用之前,计划是有一个select()
负责轮询的线程,然后将相关句柄放在ThreadQueue
线程池中的某些内容中,以实际读取和写入.(当然,我正在设计这个,以便对所使用的实际描述符集的修改select
将是线程安全的,并且仅在一个线程中发生 - 运行相同的一个,select()
因此从来没有在它运行时,显然.)
这似乎不会发生,因为句柄本身无法共享,因此轮询以及读取和写入都需要从一个线程发生.这有什么解决方法吗?我指的是跨线程的实际系统调用的分解; 很明显,有一些方法可以使用队列和缓冲区来在其他线程中生成数据并在其他线程中实际发送.
这种情况产生的一个问题是我必须给出select()
超时,并且期望它足够高,不会导致轮询相当大的描述符集的任何问题,同时足够低,不会在我的时序中引入太多延迟事件循环 - 虽然,我确实理解,如果在轮询过程中检测到实际的I/O集成员资格,select()
将提前返回,这部分缓解了问题.我宁愿有一些select()
从另一个线程中醒来的方法,但由于句柄无法共享,我不能轻易想到这样做的方法,也不能看到这样做的价值; 什么是其他线程知道何时适合唤醒select()
?
如果没有解决方法,Perl中这类服务的优秀设计模式是什么?我需要相当大的可伸缩性和并发I/O,因此出于非阻塞路由而不是仅为每个侦听套接字和/或客户端和/或服务器进程生成线程,因为许多人使用更高的 - 现在,当处理套接字时,这些级别的语言是不习惯的 - 它似乎是Java领域的一种标准做法,似乎没有人关心java.nio.*
面向系统的编程的狭隘领域.也许这只是我的印象.无论如何,我不想这样做.
因此,从经验丰富的Perl系统程序员的角度来看,这些东西应该如何组织?单片I/O线程+纯工作线程(非I/O)线程+大量队列?某种聪明的黑客?除了我已经列举的内容之外,任何线程安全都需要注意?有没有更好的办法?我在使用C语言构建此类程序方面拥有丰富的经验,但没有使用Perl习语或运行时特性.
编辑:PS我肯定发现,也许一个具有这些性能要求的程序和这个设计应该只是不用Perl编写.但我看到Perl中生成了大量非常复杂的服务,所以我不确定.
包括几个更大的设计问题,我可以提供一些方法来跨perl线程共享文件句柄.
可以传递$client
给线程启动例程或者只是在新线程中引用它:
$client = $server_socket->accept(); threads->new(\&handle_client, $client); async { handle_client($client) }; # $client will be closed only when all threads' references # to it pass out of scope.
对于Thread::Queue
设计,可以enqueue()
使用底层fd:
$q->enqueue( POSIX::dup(fileno $client) ); # we dup(2) so that $client may safely go out of scope, # closing its underlying fd but not the duplicate thereof async { my $client = IO::Handle->new_from_fd( $q->dequeue, "r+" ); handle_client($client); };
或者可以只使用fds,以及Perl的位向量形式select
.