我正在尝试开发一个服务,其中包含通过 IO::Select
同步轮询的大量客户端和服务器套接字(服务器服务以及连接到托管组件并持续存在的客户端)。 .这个想法是处理通过工作线程池产生的 I/O 和/或请求处理需求。shared
在 Perl 中使数据可跨线程共享的关键字 ( threads::shared
) 有其限制——句柄引用不在可以共享的原语中。
在我发现句柄和/或句柄引用不能共享之前,计划是有一个 select()
负责轮询的线程,然后将相关句柄放入某个 ThreadQueue
s 分布在线程池中以实际进行读取和写入。 (当然,我是这样设计的,以便对 select
使用的实际描述符集的修改是线程安全的,并且仅在一个线程中进行——运行 select()
的线程相同,因此永远不会在它运行时运行,显然。)
现在似乎不会发生这种情况,因为句柄本身无法共享,因此轮询以及读取和写入都需要在一个线程中进行。有什么解决方法吗?我指的是跨线程的实际系统调用的分解;显然,有一些方法可以使用队列和缓冲区在其他线程中生成数据并在其他线程中实际发送数据。
这种情况产生的一个问题是我必须给select()
超时,并期望它足够高,不会在轮询相当大的描述符集时引起任何问题,同时足够低,不会在我的计时事件循环中引入太多延迟 - 虽然,我确实理解,如果有实际在轮询过程中检测到 I/O 集成员身份,select()
将提前返回,这在一定程度上缓解了问题。我宁愿有一些醒来的方式select()
从另一个线程开始,但由于句柄不能共享,我无法轻易想到这样做的方法,也看不到这样做的值(value);另一个线程会知道什么时候适合唤醒 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
的位向量形式。 .
关于perl - Perl 中的非阻塞 I/O 操作是否仅限于一个线程?好的设计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1074713/