Bagaimana cara mencegat keluaran yang tidak buffer dari Proc :: Async di Raku?
Dengan potongan seperti
# Contents of ./run
my $p = Proc::Async.new: @*ARGS; react { whenever Promise.in: 5 { $p.kill }
whenever $p.stdout { say "OUT: { .chomp }" } whenever $p.ready { say "PID: $_" } whenever $p.start { say "Done" }
}
dieksekusi seperti
./run raku -e 'react whenever Supply.interval: 1 { .say }'
Saya berharap untuk melihat sesuatu seperti
PID: 1234
OUT: 0
OUT: 1
OUT: 2
OUT: 3
OUT: 4
Done
tapi sebaliknya aku mengerti
PID: 1234
OUT: 0
Done
Saya mengerti bahwa ini ada hubungannya dengan buffering: jika saya mengubah perintah itu menjadi seperti
# The $|++ disables buffering ./run perl -E '$|++; while(1) { state $i; say $i++; sleep 1 }'
Saya mendapatkan hasil yang diinginkan.
Saya tahu bahwa objek TTY IO :: Handle adalah unbuffered , dan dalam hal ini $*OUT
proses yang muncul bukanlah satu. Dan saya pernah membaca bahwa IO :: Pipa benda yang buffered "sehingga menulis tanpa membaca tidak segera memblokir" (meskipun saya tidak bisa mengatakan saya sepenuhnya mengerti apa artinya ini).
Tapi apa pun yang saya coba, saya tidak bisa mendapatkan aliran keluaran unbuffered dari Proc :: Async. Bagaimana saya melakukan ini?
Saya sudah mencoba mengikat IO :: Menangani menggunakan terbuka $proc.bind-stdout
tetapi saya masih mendapatkan masalah yang sama.
Perhatikan bahwa melakukan sesuatu seperti $proc.bind-stdout: $*OUT
itu berhasil, dalam arti bahwa objek Proc :: Async tidak lagi buffer, tetapi itu juga bukan solusi untuk masalah saya, karena saya tidak dapat memanfaatkan output sebelum keluar. Itu menyarankan kepada saya bahwa jika saya dapat mengikat Proc :: Async ke pegangan yang tidak disangga, itu harus melakukan hal yang benar. Tapi aku juga belum bisa membuatnya bekerja.
Untuk klarifikasi: seperti yang disarankan dengan contoh Perl, saya tahu saya dapat memperbaikinya dengan menonaktifkan buffering pada perintah yang akan saya sampaikan sebagai input, tetapi saya mencari cara untuk melakukan ini dari sisi yang membuat Proc: : Objek Async.
Jawaban
Proc::Async
itu sendiri tidak melakukan buffering pada data yang diterima. Namun, proses pemijahan dapat melakukan sendiri tergantung pada apa yang mereka hasilkan, dan itulah yang diamati di sini.
Banyak program membuat keputusan tentang buffering keluarannya (antara lain, seperti apakah akan memancarkan kode warna) berdasarkan apakah pegangan keluaran dipasang ke TTY (terminal). Asumsinya adalah bahwa TTY berarti manusia akan mengawasi output, dan dengan demikian latensi lebih disukai daripada throughput, jadi buffering dinonaktifkan (atau dibatasi pada buffering baris). Sebaliknya, jika outputnya berupa pipa atau file, maka asumsinya adalah bahwa latensi tidak begitu penting, dan buffering digunakan untuk mencapai kemenangan throughput yang signifikan (jauh lebih sedikit panggilan sistem untuk menulis data).
Saat kita menelurkan sesuatu dengan Proc::Async
, keluaran standar dari proses pemijahan terikat ke pipa - yang bukan TTY. Jadi program yang dipanggil dapat menggunakan ini untuk memutuskan untuk menerapkan buffering keluaran.
Jika Anda ingin memiliki ketergantungan lain, Anda dapat menjalankan program melalui. sesuatu yang memalsukan TTY, seperti unbuffer
( expect
tampaknya bagian dari paket). Berikut adalah contoh program yang mengalami buffering:
my $proc = Proc::Async.new: 'raku', '-e', 'react whenever Supply.interval(1) { .say }'; react whenever $proc.stdout {
.print
}
Kami hanya melihat 0
dan kemudian harus menunggu lama untuk lebih banyak keluaran. Menjalankannya melalui unbuffer
:
my $proc = Proc::Async.new: 'unbuffer', 'raku', '-e', 'react whenever Supply.interval(1) { .say }'; react whenever $proc.stdout {
.print
}
Berarti kita melihat keluaran angka setiap detik.
Bisakah Raku memberikan solusi bawaan untuk ini suatu hari nanti? Ya - dengan melakukan "keajaiban" yang unbuffer
dilakukannya sendiri (saya kira mengalokasikan pty
sejenis TTY palsu). Ini tidak sepele - meskipun sedang dieksplorasi oleh pengembang libuv ; setidaknya sejauh Rakudo di MoarVM berjalan, saat ada rilis libuv yang tersedia menawarkan fitur seperti itu, kami akan bekerja untuk mengeksposnya.
Anda dapat menyetel .out-buffer
pegangan (seperti $*OUT
atau $*ERR
) ke 0:
$ ./run raku -e '$*OUT.out-buffer = 0; react whenever Supply.interval: 1 { .say }'
PID: 11340
OUT: 0
OUT: 1
OUT: 2
OUT: 3
OUT: 4
Done