You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

为何pipe.write会阻塞线程?附示例程序及问题复现

Why does pipe.write block the thread in this Ruby code?

Let's break down your problem step by step, including why the block happens and how to fix it:

The Problem Context

You're using this Ruby code to read a binary file and pipe its content to the cat command, but the to.write(buff) call hangs indefinitely:

from = File.open('test.bin', 'rb')
to = IO.popen('cat', 'w+b')
i = 0
while buff = from.read(4096)
  to.write(buff) # blocks here
  i += 1
  print "#{i}, #{buff.size} bytes read \r"
end

For testing, you create the input file with: fallocate -l 1MB test.bin

Why the Block Occurs

This is a classic case of pipe buffer deadlock on Unix/Linux systems:

  • Pipes have a fixed-size buffer (usually ~64KB, depending on the OS). When you write to the pipe, the system will block your write call if the buffer is full, until the reading end consumes some data to free up space.
  • The cat process reads from the pipe (its stdin) and writes the content to its stdout. But in your code, you opened the pipe in w+b mode but never read from to (the pipe's stdout side). This means cat's stdout has nowhere to go—eventually, cat will block on writing to its stdout, which in turn means it stops reading from the pipe.
  • With cat not consuming data from the pipe, your Ruby process's to.write(buff) call will hang once the pipe buffer is completely full.

Fixes You Can Use

There are a few straightforward solutions to break this deadlock:

1. Spawn a Thread to Consume the Pipe's Output

Create a separate thread to read from cat's output, so cat can keep processing input and freeing up the pipe buffer:

from = File.open('test.bin', 'rb')
to = IO.popen('cat', 'w+b')

# Thread to consume cat's output and prevent it from blocking
Thread.new do
  while output = to.read(4096)
    # You can print the output here, or discard it if you don't need it
    # puts output
  end
end

i = 0
while buff = from.read(4096)
  to.write(buff)
  i += 1
  print "#{i}, #{buff.size} bytes read \r"
end
to.close # Close the write end so cat knows there's no more data

2. Redirect cat's Output to /dev/null

If you don't care about cat's output at all, redirect its stdout to /dev/null—this lets cat run without blocking on output:

from = File.open('test.bin', 'rb')
# Redirect cat's stdout to /dev/null to avoid blocking
to = IO.popen('cat > /dev/null', 'w')
i = 0
while buff = from.read(4096)
  to.write(buff)
  i += 1
  print "#{i}, #{buff.size} bytes read \r"
end
to.close

3. Use IO.copy_stream for Simplified Data Copying

Ruby's built-in IO.copy_stream handles buffer management and pipe mechanics under the hood, making your code shorter and avoiding the blocking issue entirely:

from = File.open('test.bin', 'rb')
to = IO.popen('cat > /dev/null', 'w')
IO.copy_stream(from, to)
from.close
to.close

Summary

The core issue is mutual blocking: your write operation fills the pipe buffer, and cat blocks on its own output because you're not consuming it. By ensuring cat's output is handled (either read in a thread or discarded), you break the deadlock and let the data flow through the pipe properly.

内容的提问来源于stack exchange,提问作者Roman Pushkin

火山引擎 最新活动