【问题标题】:Are nonblocking I/O operations in Perl limited to one thread? Good design?Perl 中的非阻塞 I/O 操作是否仅限于一个线程?好的设计?
【发布时间】:2009-07-02 14:27:21
【问题描述】:

我正在尝试开发一种服务,该服务包含通过IO::Select 同步轮询的大量客户端和服务器套接字(服务器服务以及连接到托管组件并持续存在的客户端)。这个想法是处理通过工作线程池产生的 I/O 和/或请求处理需求。

在 Perl (threads::shared) 中使数据可跨线程共享的 shared 关键字有其局限性——句柄引用不在可共享的原语中。

在我发现无法共享句柄和/或句柄引用之前,计划是有一个 select() 线程来处理轮询,然后将相关句柄放在某个 ThreadQueues 中,分布在一个线程池来实际进行读写。 (当然,我是这样设计的,以便对select 使用的实际描述符集的修改将是线程安全的,并且只发生在一个线程中——运行select() 的同一线程,因此永远不会很明显,正在运行。)

这似乎不会发生,因为句柄本身不能共享,因此轮询以及读取和写入都需要从一个线程进行。有什么解决方法吗?我指的是跨线程的实际系统调用的分解;显然,有一些方法可以使用队列和缓冲区让数据在其他线程中生成并在其他线程中实际发送。

这种情况产生的一个问题是我必须给select() 一个超时,并期望它足够高而不会导致轮询相当大的一组描述符时出现任何问题,同时又足够低而不会引入我的计时事件循环延迟过多 - 不过,我明白如果在轮询过程中检测到实际的 I/O 集成员资格,select() 将提前返回,这在一定程度上缓解了问题。我宁愿有某种方法从另一个线程唤醒select(),但由于无法共享句柄,我无法轻易想到这样做的方法,也看不到这样做的价值;无论如何,其他线程会知道什么时候适合唤醒select()

如果没有解决方法,那么在 Perl 中,这种类型的服务有什么好的设计模式?我需要相当高的可扩展性和并发 I/O,因此走的是非阻塞路线,而不是仅仅为每个侦听套接字和/或客户端和/或服务器进程生成线程,因为许多人使用更高-如今,在处理套接字时不习惯使用级别语言——这似乎是 Java 领域的一种标准做法,在面向系统编程的狭窄领域之外,似乎没有人关心java.nio.*。也许这只是我的印象。反正我不想那样做。

那么,从经验丰富的 Perl 系统程序员的角度来看,应该如何组织这些东西?单片 I/O 线程 + 纯工作(非 I/O)线程 + 大量队列?某种聪明的hack?除了我已经列举的之外,还有什么需要注意的线程安全问题吗?有没有更好的办法?我在用 C 语言构建此类程序方面拥有丰富的经验,但没有 Perl 习惯用法或运行时特性。

编辑:附注我确实想到,也许一个具有这些性能要求和这种设计的程序根本不应该用 Perl 编写。但是我看到 Perl 产生了很多非常复杂的服务,所以我不确定。

【问题讨论】:

    标签: perl multithreading sockets nonblocking


    【解决方案1】:

    排除您的几个更大的设计问题,我可以提供一些方法来跨 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 的位向量形式。

    【讨论】:

    • 有趣的建议。我特别喜欢传递底层 FD,然后稍后再用它构建一个句柄的方法。您能建议一种方法来初始化文件句柄,然后稍后在线程内将其分配给 IO::Socket 吗?我宁愿不在一个线程中创建套接字,然后在另一个线程中操作它们;是否可以做一些通用的事情,比如 IO::Handle->new?
    • Alex,没有伪代码,我不确定我是否理解你的问题的细节。但是,'use IO::Socket::INET' 和 'IO::Socket::INET->new_from_fd($my_duplicated_fd, "r+")' 会得到一个 IO::Socket 对象。
    • 哦,我没有意识到 IO::Socket::INET 是 IO::Handle 的子类。有道理。
    猜你喜欢
    • 2019-09-24
    • 1970-01-01
    • 2013-05-31
    • 2017-12-20
    • 2014-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    相关资源
    最近更新 更多