【问题标题】:Read and Write in the same file with different process使用不同的进程在同一个文件中读取和写入
【发布时间】:2010-05-11 10:47:53
【问题描述】:

我已经写了这两个程序。一个程序是同时将内容写入文本文件。另一个程序同时读取该内容。

但是两个程序应该同时运行。对我来说,程序是正确写入文件。但是另一个程序没有读取该文件。

我知道一旦写入过程完成,硬盘中就只有数据了。然后另一个进程可以读取数据。

但我希望在单个文件中使用不同的进程同时读取和写入。我怎样才能做到这一点?

请帮助我。 以下代码将内容写入文件中

sub generate_random_string
{
    my $length_of_randomstring=shift;# the length of 
       # the random string to generate

    my @chars=('a'..'z','A'..'Z','0'..'9','_');
    my $random_string;
    foreach (1..$length_of_randomstring)
    {
        # rand @chars will generate a random 
        # number between 0 and scalar @chars
        $random_string.=$chars[rand @chars];
    }
    return $random_string;
}
#Generate the random string
open (FH,">>file.txt")or die "Can't Open";
while(1)
{
my $random_string=&generate_random_string(20);
sleep(1);
#print $random_string."\n";
print FH $random_string."\n";
}

以下代码是读取内容。这是另一个过程

 open (FH,"<file.txt") or die "Can't Open";
              print "Open the file Successfully\n\n";
              while(<FH>)
              {
                  print "$_\n";
              }

【问题讨论】:

  • 你已经尝试过什么。你能分享一下你试过的代码吗?
  • 尝试非阻塞模式读取文件
  • 您可能希望先在写入程序中关闭文件,然后再在读取程序中打开文件。您如何确保仅在写入后发生读取?
  • 可能与您正在做的事情相关:stackoverflow.com/questions/1425223/…

标签: perl


【解决方案1】:

您可以使用如下所示的详细合作协议。两端readerwriter 使用TakeTurns 模块中的通用代码来处理诸如锁定和锁定文件所在的位置等繁琐的细节。客户只需要指定当他们对文件有独占访问权时他们想要做什么。

阅读器

#! /usr/bin/perl

use warnings;
use strict;

use TakeTurns;

my $runs = 0;
reader "file.txt" =>
       sub {
         my($fh) = @_;
         my @lines = <$fh>;
         print map "got: $_", @lines;
         ++$runs <= 10;
       };

作家

#! /usr/bin/perl

use warnings;
use strict;

use TakeTurns;

my $n = 10;
my @chars = ('a'..'z','A'..'Z','0'..'9','_');

writer "file.txt" =>
       sub { my($fh) = @_;
             print $fh join("" => map $chars[rand @chars], 1..$n), "\n"
               or warn "$0: print: $!";
           };

TakeTurns 模块在工作中是execute-around

package TakeTurns;                               
                                                 
use warnings;                                    
use strict;                                      
                                                 
use Exporter 'import';                           
use Fcntl qw/ :DEFAULT :flock /;                 
                                                 
our @EXPORT = qw/ reader writer /;               
my $LOCKFILE = "/tmp/taketurns.lock";            
                                                 
sub _loop ($&) {
  my($path,$action) = @_;
  while (1) {
    sysopen my $lock, $LOCKFILE, O_RDWR|O_CREAT
                                   or die "sysopen: $!";
    flock $lock, LOCK_EX           or die "flock: $!";
    my $continue = $action->();
    close $lock                    or die "close: $!";
    return unless $continue;
    sleep 0;
  }
}

sub writer {
  my($path,$w) = @_;
  _loop $path =>
        sub {
          open my $fh, ">", $path   or die "open $path: $!";
          my $continue = $w->($fh);
          close $fh                 or die "close $path: $!";
          $continue;
        };
}

sub reader {
  my($path,$r) = @_;
  _loop $path =>
        sub {
          open my $fh, "<", $path        or die "open $path: $!";
          my $continue = $r->($fh);
          close $fh                      or die "close $path: $!";
          $continue;
        };
}

1;

样本输出:

得到:1Upem0iSfY
得到:qAALqegWS5
得到:88RayL3XZw
得到:NRB7POLdu6
得到:IfqC8XeWN6
得到:mgeA6sNEpY
得到:2TeiF5sDqy
得到:S2ksYEkXsJ
得到:zToPYkGPJ5
得到:6VXu6ut1Tq
得到:ex0wYvp9Y8

即使您遇到了这么多麻烦,仍然存在问题。该协议不可靠,因此reader 无法保证看到writer 发送的所有消息。在没有writer 处于活动状态的情况下,reader 满足于一遍又一遍地阅读同一条消息。

您可以添加所有这些,但更明智的方法是使用操作系统已经提供的抽象。

例如,Unix named pipes 似乎与您想要的非常接近,请注意代码是多么简单:

阅读

#! /usr/bin/perl

use warnings;
use strict;

my $pipe = "/tmp/mypipe";
system "mknod $pipe p 2>/dev/null";

open my $fh, "<", $pipe or die "$0: open $pipe: $!";

while (<$fh>) {
  print "got: $_";
  sleep 0;
}

#! /usr/bin/perl

use warnings;
use strict;

my $pipe = "/tmp/mypipe";
system "mknod $pipe p 2>/dev/null";

open my $fh, ">", $pipe or die "$0: open $pipe: $!";

my $n = 10;
my @chars = ('a'..'z','A'..'Z','0'..'9','_');

while (1) {
  print $fh join("" => map $chars[rand @chars], 1..$n), "\n"
    or warn "$0: print: $!";
}

两端都尝试使用mknod 创建管道,因为它们没有其他同步方法。至少有一个会失败,但只要管道存在,我们就不管。

如您所见,所有等待的机器都由系统处理,因此您可以做自己关心的事情:读取和写入消息。

【讨论】:

    【解决方案2】:

    这行得通。

    作者:

    use IO::File ();
    
    sub generate_random_string {...}; # same as above
    
    my $file_name = 'file.txt';
    my $handle = IO::File->new($file_name, 'a');
    die "Could not append to $file_name: $!" unless $handle;
    $handle->autoflush(1);
    
    while (1) {
        $handle->say(generate_random_string(20));
    }
    

    读者:

    use IO::File qw();
    
    my $file_name = 'file.txt';
    my $handle = IO::File->new($file_name, 'r');
    die "Could not read $file_name: $!" unless $handle;
    
    STDOUT->autoflush(1);
    while (defined (my $line = $handle->getline)) {
        STDOUT->print($line);
    }
    

    【讨论】:

      【解决方案3】:

      您使用的是 Windows 还是 *nix?您可以通过使用 tail 在 *nix 上将类似的内容串在一起,以获取写入文件的输出。在 Windows 上,您可以使用 FILE_SHARE_READ 和/或 FILE_SHARE_WRITE 调用 CreateFile(),以便在您打开文件进行读/写时允许其他人访问该文件。您可能需要定期检查文件大小是否已更改,以便知道何时读取(我在这里不是 100% 确定。)

      另一个选项是内存映射文件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-13
        • 2018-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-09
        相关资源
        最近更新 更多