ฉันจะสกัดกั้นเอาต์พุตที่ไม่มีบัฟเฟอร์ของ Proc :: Async ใน Raku ได้อย่างไร
ด้วยตัวอย่างเช่น
# 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" }
}
ดำเนินการเช่น
./run raku -e 'react whenever Supply.interval: 1 { .say }'
ฉันคาดว่าจะได้เห็นบางสิ่งบางอย่างเช่น
PID: 1234
OUT: 0
OUT: 1
OUT: 2
OUT: 3
OUT: 4
Done
แต่ฉันเห็นแทน
PID: 1234
OUT: 0
Done
ฉันเข้าใจว่าสิ่งนี้เกี่ยวข้องกับการบัฟเฟอร์: ถ้าฉันเปลี่ยนคำสั่งนั้นเป็นไฟล์
# The $|++ disables buffering ./run perl -E '$|++; while(1) { state $i; say $i++; sleep 1 }'
ฉันได้ผลลัพธ์ที่ต้องการ
ฉันรู้ว่าอ็อบเจ็กต์ TTY IO :: Handle ไม่ถูกบัฟเฟอร์และในกรณีนี้$*OUT
กระบวนการที่เกิดไม่ใช่กระบวนการเดียว และฉันได้อ่านพบว่าIO :: ไพพ์อ็อบเจกต์ถูกบัฟเฟอร์ "ดังนั้นการเขียนโดยไม่มีการอ่านจะไม่ถูกบล็อกทันที" (แม้ว่าฉันจะไม่สามารถพูดได้ว่าฉันเข้าใจทั้งหมดว่าหมายถึงอะไรก็ตาม)
แต่ไม่ว่าฉันจะพยายามอย่างไรฉันก็ไม่สามารถรับสตรีมเอาต์พุตที่ไม่มีบัฟเฟอร์ของ Proc :: Async ได้ ฉันต้องทำอย่างไร
ฉันได้ลองผูก IO :: Handle แบบเปิดโดยใช้$proc.bind-stdout
แต่ฉันยังคงได้รับปัญหาเดิม
โปรดทราบว่าการทำบางสิ่งบางอย่างได้$proc.bind-stdout: $*OUT
ผลในแง่ที่วัตถุ Proc :: Async ไม่บัฟเฟอร์อีกต่อไป แต่ก็ไม่ใช่วิธีแก้ปัญหาของฉันเพราะฉันไม่สามารถแตะที่ผลลัพธ์ก่อนที่มันจะออกไป ฉันแนะนำว่าถ้าฉันสามารถผูก Proc :: Async กับแฮนเดิลที่ไม่มีบัฟเฟอร์ได้ก็ควรทำในสิ่งที่ถูกต้อง แต่ฉันยังไม่สามารถทำงานนั้นได้
เพื่อความชัดเจน: ตามที่แนะนำในตัวอย่าง Perl ฉันรู้ว่าฉันสามารถแก้ไขได้โดยปิดการใช้งานบัฟเฟอร์ของคำสั่งที่ฉันจะส่งเป็นอินพุต แต่ฉันกำลังมองหาวิธีที่จะทำสิ่งนี้จากด้านที่สร้าง Proc: : วัตถุ Async
คำตอบ
Proc::Async
ตัวมันเองไม่ได้ทำการบัฟเฟอร์กับข้อมูลที่ได้รับ อย่างไรก็ตามกระบวนการที่เกิดขึ้นอาจทำเองได้ขึ้นอยู่กับสิ่งที่ส่งออกไปและนั่นคือสิ่งที่สังเกตได้ที่นี่
หลายโปรแกรมทำการตัดสินใจเกี่ยวกับการบัฟเฟอร์เอาต์พุต (นอกเหนือจากสิ่งอื่น ๆ เช่นว่าจะปล่อยรหัสสีหรือไม่) โดยขึ้นอยู่กับว่าที่จับเอาต์พุตแนบกับ TTY หรือไม่ สมมติฐานคือ TTY หมายถึงมนุษย์กำลังจะดูผลลัพธ์ดังนั้นเวลาแฝงจึงเป็นที่ต้องการของปริมาณงานดังนั้นการบัฟเฟอร์จึงถูกปิดใช้งาน (หรือ จำกัด เฉพาะการบัฟเฟอร์บรรทัด) ในทางกลับกันหากเอาต์พุตกำลังไปที่ไพพ์หรือไฟล์ข้อสันนิษฐานก็คือเวลาแฝงนั้นไม่สำคัญนักและการบัฟเฟอร์จะถูกใช้เพื่อให้ได้อัตราการรับส่งข้อมูลที่สำคัญ (ระบบเรียกให้เขียนข้อมูลน้อยลงมาก)
เมื่อเราวางไข่ด้วยProc::Async
ผลลัพธ์มาตรฐานของกระบวนการสร้างจะถูกผูกไว้กับท่อซึ่งไม่ใช่ TTY ดังนั้นโปรแกรมที่เรียกใช้อาจใช้สิ่งนี้เพื่อตัดสินใจใช้การบัฟเฟอร์เอาต์พุต
หากคุณยินดีที่จะมีการพึ่งพาอื่นคุณสามารถเรียกใช้โปรแกรมผ่านทาง. สิ่งที่ทำให้เกิด TTY เช่นunbuffer
( ดูเหมือนเป็นส่วนหนึ่งของexpect
แพ็คเกจ) นี่คือตัวอย่างของโปรแกรมที่มีปัญหาจากการบัฟเฟอร์:
my $proc = Proc::Async.new: 'raku', '-e', 'react whenever Supply.interval(1) { .say }'; react whenever $proc.stdout {
.print
}
เราเห็นเพียง a 0
แล้วต้องรอเป็นเวลานานเพื่อให้ได้ผลผลิตมากขึ้น เรียกใช้ผ่านunbuffer
:
my $proc = Proc::Async.new: 'unbuffer', 'raku', '-e', 'react whenever Supply.interval(1) { .say }'; react whenever $proc.stdout {
.print
}
หมายความว่าเราเห็นเอาต์พุตตัวเลขทุกวินาที
ราคุสามารถจัดหาโซลูชันในตัวให้กับวันนี้ได้หรือไม่? ใช่ - โดยการทำ "เวทมนตร์" ที่unbuffer
ตัวเองทำ (ฉันคิดว่าจะจัดสรรpty
TTY ปลอม) นี้ไม่ได้เป็นที่น่ารำคาญ - แม้ว่ามันจะถูกสำรวจโดยนักพัฒนา libuv ; อย่างน้อยที่สุดเท่าที่ Rakudo บน MoarVM ดำเนินไปในขณะที่มีการเปิดตัว libuv ซึ่งนำเสนอคุณลักษณะดังกล่าวเราจะดำเนินการเปิดเผย
คุณสามารถตั้งค่า.out-buffer
แฮนเดิล (เช่น$*OUT
หรือ$*ERR
) เป็น 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