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

时间:2015-02-20 18:50:52

标签: perl nonblocking

我正在学习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;
}

2 个答案:

答案 0 :(得分:4)

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

  • 非阻塞I / O:当它无法立即完成时,操作不会阻塞,但它会尽可能多地执行,然后返回。
  • 异步I / O:操作将在后台完成,一旦完成,将通知应用程序。

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

要使用非阻塞,你必须将文件描述符标记为非阻塞,然后像正常情况一样对它进行读,写,连接,接受等,除非它永远不会阻塞但返回EAGAIN / EWOULDBLOCK如果操作无法立即执行。要等到文件描述符再次可读/可写,您可以使用selectpollkqueue或类似的机制。

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

  • 在UNIX和Windows上将文件描述符设置为非阻塞是不同的。 IO::Socket->blocking为您处理主要差异。
  • 今天的UNIX EAGAINEWOULDBLOCK相同,但在Windows上则不同,您通常使用EWOULDBLOCK
  • 同样,非阻塞对普通文件不起作用。它可能不会抛出错误,但只会阻止。
  • 您必须注意是否要对文件描述符使用非阻塞,这些文件描述符不完全驻留在内核中(例如使用SSL套接字)。有关详细信息,请查看documentation of IO::Socket::SSL

答案 1 :(得分:2)

我会稍微考虑一下 - 我不太熟悉这样的非阻塞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();