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,因为我的方法往往是 thread
或 fork
并且做就那样
不过,非阻塞式写入文件并不是那么常见 - 在处理小文件时通常不相关,因此您的测试用例可能无法正常工作。我建议您瞄准几兆字节的写入,只是为了能够看到它发生。
至于线程方法 - 我倾向于这样做:
#!/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,您可以使用 select
、poll
、kqueue
或类似的机制。
这是一个简短的介绍。您需要注意的一些事项:
- 在 UNIX 和 Windows 上将文件描述符设置为非阻塞是不同的。
IO::Socket->blocking
为您处理主要差异。
- 在当今的 UNIX 上,
EAGAIN
与 EWOULDBLOCK
相同,但在 Windows 上,这是不同的,您通常必须使用 EWOULDBLOCK
。
- 同样,非阻塞不适用于常规文件。它可能不会抛出错误,但只会阻塞。
- 如果要对不完全驻留在内核中的文件描述符(如 SSL 套接字)使用非阻塞,则必须小心。查看 documentation of IO::Socket::SSL 了解更多信息。
我正在学习 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,因为我的方法往往是 thread
或 fork
并且做就那样
不过,非阻塞式写入文件并不是那么常见 - 在处理小文件时通常不相关,因此您的测试用例可能无法正常工作。我建议您瞄准几兆字节的写入,只是为了能够看到它发生。
至于线程方法 - 我倾向于这样做:
#!/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,您可以使用 select
、poll
、kqueue
或类似的机制。
这是一个简短的介绍。您需要注意的一些事项:
- 在 UNIX 和 Windows 上将文件描述符设置为非阻塞是不同的。
IO::Socket->blocking
为您处理主要差异。 - 在当今的 UNIX 上,
EAGAIN
与EWOULDBLOCK
相同,但在 Windows 上,这是不同的,您通常必须使用EWOULDBLOCK
。 - 同样,非阻塞不适用于常规文件。它可能不会抛出错误,但只会阻塞。
- 如果要对不完全驻留在内核中的文件描述符(如 SSL 套接字)使用非阻塞,则必须小心。查看 documentation of IO::Socket::SSL 了解更多信息。