Perl 检查非阻塞文件读/写是否完成

Perl check if nonblocking file read/ write has finished

我正在学习 perl 中的非阻塞 io。我有以下脚本,我试图以非阻塞方式写入文件。

这是正确的做法吗?如果不是,正确的方法是什么?我在模拟不是瞬间发生的写入时遇到问题,所以我无法真正测试它是否有效。

use strict;
use warnings;

use POSIX qw(:errno_h);

open(my $fh, ">>", "b.txt") or die "$! \n";

my $buffer = "aaaaaaaaaaaaaaaaaaaaaaa\n";

my $rv = syswrite($fh, $buffer, length $buffer);
#start more non blocking operations
while(1)
{
    if (!defined($rv) && $! == EAGAIN) {
        print "Would block \n";
    } elsif ($rv != length $buffer) {
        print "Incomplete write \n";
    } else {
    print "Successfull write so we can do what we want with the read data\n";
    }
   #check the other operations

   #sleep for a bit
    sleep 1;
}

我会有点切线 - 我不太熟悉那样做非阻塞 IO,因为我的方法往往是 threadfork 并且做就那样

不过,非阻塞式写入文件并不是那么常见 - 在处理小文件时通常不相关,因此您的测试用例可能无法正常工作。我建议您瞄准几兆字节的写入,只是为了能够看到它发生。

至于线程方法 - 我倾向于这样做:

#!/usr/bin/perl

use strict;
use warnings;

use threads;
use Thread::Queue;

my $output_q = Thread::Queue->new();

sub writer {
    open( my $output_fh, ">", "output_file.txt" ) or die $!;
    while ( my $line = $output_q->dequeue ) {
        print {$output_fh} $line;
    }
    close($output_fh) or die $!;
}

## main bit.

my $writer = threads->create( \&writer );

my $done = 0;

while ( not $done ) {
    $output_q->enqueue( "a" x 1024 );

    # do something else.
    #repeat until $done

}

$output_q->end();
$writer->join();

首先,非阻塞 I/O 适用于套接字和管道,但不适用于常规文件。这不是 Perl 的限制,而是底层系统的限制。您想要的常规文件不是非阻塞的而是异步的 I/O:

  • 非阻塞I/O:操作不能立即完成时不会阻塞,但会尽可能多地完成然后return。
  • 异步I/O:操作会在后台完成,完成后会通知应用程序。

不幸的是,对真正异步的支持 I/O 是特定于系统的,通常没有得到很好的支持,所以您需要在这里使用线程(同样,这不是 Perl 的问题,而是底层系统的问题)。您可能想看一下 IO::AIO,它试图通过使用系统支持的任何内容来给您带来异步 I/O 的感觉。但还要注意它不是很有效,如果你可以直接使用非阻塞套接字(即使用套接字而不是常规文件)你应该这样做。

要使用非阻塞,你必须将文件描述符标记为非阻塞,然后像往常一样在其上读取、写入、连接、接受等,除了它永远不会阻塞,但 return EAGAIN/EWOULDBLOCK 如果操作不能立即执行。要等到文件描述符再次变为 readable/writable,您可以使用 selectpollkqueue 或类似的机制。

这是一个简短的介绍。您需要注意的一些事项:

  • 在 UNIX 和 Windows 上将文件描述符设置为非阻塞是不同的。 IO::Socket->blocking 为您处理主要差异。
  • 在当今的 UNIX 上,EAGAINEWOULDBLOCK 相同,但在 Windows 上,这是不同的,您通常必须使用 EWOULDBLOCK
  • 同样,非阻塞不适用于常规文件。它可能不会抛出错误,但只会阻塞。
  • 如果要对不完全驻留在内核中的文件描述符(如 SSL 套接字)使用非阻塞,则必须小心。查看 documentation of IO::Socket::SSL 了解更多信息。