Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read and Write in the same file with different process

Tags:

perl

I have written the two program. One program is write the content to the text file simultaneously. Another program is read that content simultaneously.

But both the program should run at the same time. For me the program is write the file is correctly. But another program is not read the file.

I know that once the write process is completed than only the data will be stored in the hard disk. Then another process can read the data.

But I want both read and write same time with different process in the single file. How can I do that?

Please help me. The following code write the content in the file

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";
}

The following code is read the content. This is another process

 open (FH,"<file.txt") or die "Can't Open";
              print "Open the file Successfully\n\n";
              while(<FH>)
              {
                  print "$_\n";
              }
like image 423
muruga Avatar asked May 11 '10 10:05

muruga


2 Answers

You might use an elaborate cooperation protocol such as in the following. Both ends, reader and writer, use common code in the TakeTurns module that handles fussy details such as locking and where the lock file lives. The clients need only specify what they want to do when they have exclusive access to the file.

reader

#! /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;
       };

writer

#! /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: $!";
           };

The TakeTurns module is execute-around at work:

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;

Sample output:

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

Even though you went to so much trouble, there are still issues. The protocol is unreliable, so reader has no guarantee of seeing all messages that writer sends. With no writer active, reader is content to read the same message over and over.

You could add all this, but a more sensible approach would be using abstractions the operating system provides already.

For example, Unix named pipes seem to be a pretty close match to what you want, and note how simple the code is:

pread

#! /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;
}

pwrite

#! /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: $!";
}

Both ends attempt to create the pipe using mknod because they have no other method of synchronization. At least one will fail, but we don't care as long as the pipe exists.

As you can see, all the waiting machinery is handled by the system, so you do what you care about: reading and writing messages.

like image 169
Greg Bacon Avatar answered Oct 12 '22 23:10

Greg Bacon


This works.

The writer:

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));
}

The reader:

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);
}
like image 43
daxim Avatar answered Oct 12 '22 23:10

daxim