Verwenden Sie die Tatsache, dass die Rohrleitung blockiert, wenn sie voll ist: Führen Sie die fixed_handler
in einem gegabelten Prozess aus, wobei der Rückruf über eine Pipe zurück zum übergeordneten Element geschrieben wird. Während der Elternteil nach einem Lesen verarbeitet wird, wird die Pipe blockiert, wenn sie voll ist und der Schreiber wartet. Um dies zu erleichtern, schreiben Sie eine zusätzliche leere Zeichenfolge, um die Pipe zu füllen.
use warnings;
use strict;
use feature 'say';
sub fixed_handler {
my $callback = shift;
#state $count = 1; # would solve the problem
my $count = 1;
for (1..4) { $callback->($count++) }
}
pipe my $reader, my $writer or die "Can't open pipe: $!";
$writer->autoflush(1);
$reader->autoflush(1);
my $fill_buff = ' ' x 100_000; # (64_656 - 3); # see text
my $iter = sub {
my $data = shift;
say "\twrite on pipe ... ($data)";
say $writer $data;
say $writer $fill_buff; # (over)fill the buffer
};
my $pid = fork // die "Can't fork: $!"; #/
if ($pid == 0) {
close $reader;
fixed_handler($iter);
close $writer;
exit;
}
close $writer;
say "Parent: started kid $pid";
while (my $recd = <$reader>) {
next if $recd !~ /\S/; # throw out the filler
chomp $recd;
say "got: $recd";
sleep 1;
}
my $gone = waitpid $pid, 0;
if ($gone > 0) { say "Child $gone exited with: $?" }
elsif ($gone < 0) { say "No such process: $gone" }
Ausgabe
Parent: started kid 13555
write on pipe ... (1)
got: 1
write on pipe ... (2)
got: 2
write on pipe ... (3)
got: 3
write on pipe ... (4)
got: 4
Child 13555 exited with: 0
Zuerst würde der Schriftsteller Druck halten, bis er den Puffer füllt. & Dolch; Dann, wenn der Leser eine Zeile erhält, setzt der Schreiber eine andere (oder zwei, wenn die Länge der Ausdrucke variiert) usw. Wenn dies in Ordnung ist, entfernen Sie say $writer $fill_buff;
. Dann in der Ausgabe sehen wir alle write on pipe
Zeilen zuerst, dann Eltern Drucke gehen. Eine übliche Puffergröße ist heutzutage 64K.
Allerdings wird uns gesagt, dass jeder Schritt von file_handler
Zeit braucht und so würden wir auf Tausende solcher Schritte warten, bevor wir die Verarbeitung im Parent starten (abhängig von der Größe jedes Schreibvorgangs), bis der Puffer gefüllt ist und der Der Schreiber wird bei jedem Lesevorgang blockiert.
Ein Ausweg daraus ist, eine extra Zeichenfolge zu schreiben, lange genug, um den Puffer zu füllen, und es im Lesegerät zu verwerfen. Ich fand es schwierig, die genaue Länge dafür zu bekommen. Zum einen unterscheidet sich der im Programm gefundene Puffer von
my $cnt; while (1) { ++$cnt; print $writer ' '; print "\r$cnt" } # reader sleeps
in ähnlicher Weise von dem in der Befehlszeile gefundenen. Auch damit bekomme ich (manchmal) "Doppelschreibversuche". Während , dass kein Show-Stopper sein kann, ging ich mit 100K
, um sicherzustellen, dass es zu füllen.
Beispiele für Puffergrößen finden Sie unter this post.
Eine andere Möglichkeit besteht darin, die Puffergröße der Pipe unter Verwendung von IO::Handle::setvbuf
festzulegen. Jedoch lief ich in "Nicht implementiert auf dieser Architektur" (auf Produktionsmaschinen) und so würde ich nicht darüber nachdenken.
Spielerisch mit Pufferung wird natürlich die Kommunikation sehr verlangsamen.
Dies implementiert die Idee von melpomene 's Kommentare.
& dolch; Mit "Puffer" beziehe ich mich auf den Puffer der Pipe (die Menge der vor den Pipes geschriebenen Daten, wenn auf der anderen Seite keine Daten gelesen werden). Das sind andere Puffer, die beteiligt sind, aber hier nicht so relevant sind.
Haben Sie Zugang zu $ Callback? – simbabque
@simbabque Sie (wir?) Schreiben den anonymen Sub, der in '$ callback' verschoben ist ...? – zdim
Es gibt https://metacpan.org/pod/Return::MultiLevel, was hilfreich sein könnte, aber solange '$ count' innerhalb von' my_fixed_handler' lexikalisch ist, wird es nicht viel helfen. Jedes Mal, wenn du 'my_fixed_handler' anrufst, wird es von vorn beginnen, also wird es jedes Mal, wenn du es wieder zurückbringst," 1 "geben. Wie soll der $ Callback die 'while (1)' Schleife durchbrechen? – simbabque