如何在mRuby中实现$stdin.gets()的非阻塞读取?
在mRuby中实现$stdin的非阻塞读取
确实,mRuby砍掉了Ruby标准库中的Thread和Queue,mruby-thread也没法满足非阻塞读stdin的需求,不过我们可以借助底层的文件操作来实现,不用写自定义扩展。下面给你两个实用的方案:
方案一:设置stdin为非阻塞模式
这个思路是通过修改stdin的文件描述符属性,让它变成非阻塞模式,读取时如果没有输入就抛出Errno::EAGAIN异常,我们捕获这个异常就可以继续执行其他逻辑。
# 获取stdin的文件描述符 fd = $stdin.fileno # 获取当前文件状态标志,添加非阻塞标志 flags = Fcntl.fcntl(fd, Fcntl::F_GETFL) Fcntl.fcntl(fd, Fcntl::F_SETFL, flags | Fcntl::O_NONBLOCK) # 封装非阻塞读取方法 def non_blocking_gets begin $stdin.gets rescue Errno::EAGAIN # 无输入时返回nil nil end end # 使用示例 loop do input = non_blocking_gets if input puts "收到输入: #{input.chomp}" break if input.chomp == 'exit' end # 这里可以放你的业务逻辑 puts "处理其他任务中..." sleep 0.5 end
不过要注意,默认的gets是按行读取的,如果输入没有换行符,即使有部分数据也会返回nil。如果需要处理不完整的输入,可以自己维护一个缓冲区:
fd = $stdin.fileno flags = Fcntl.fcntl(fd, Fcntl::F_GETFL) Fcntl.fcntl(fd, Fcntl::F_SETFL, flags | Fcntl::O_NONBLOCK) @input_buffer = "" def non_blocking_gets begin # 每次读取最多1024字节的数据 data = $stdin.read(1024) @input_buffer += data if data rescue Errno::EAGAIN # 没有新数据,跳过 end # 检查缓冲区里是否有完整的行 if line_end = @input_buffer.index("\n") line = @input_buffer[0..line_end] @input_buffer = @input_buffer[line_end + 1..-1] || "" line else nil end end # 使用示例 loop do line = non_blocking_gets if line puts "读取到完整行: #{line.chomp}" break if line.chomp == 'exit' end puts "执行其他操作..." sleep 0.5 end
方案二:用IO::Select检查可读状态
如果你不想修改stdin的阻塞模式,可以用IO::Select来监听stdin是否有可读数据,超时设为0就是非阻塞检查,只有当有数据时才去读取。
def non_blocking_gets # 创建监听stdin的选择器 read_monitor = IO::Select.new([$stdin]) # 非阻塞检查是否有可读数据 ready = read_monitor.select(0) ready ? $stdin.gets : nil end # 使用示例 loop do input = non_blocking_gets if input puts "收到输入: #{input.chomp}" break if input.chomp == 'exit' end puts "等待输入的同时处理其他事情..." sleep 0.5 end
注意事项
- 确保你的mRuby编译时包含了
mruby-fcntl和mruby-io模块,这两个都是标准库的一部分,一般默认编译会包含,如果没有的话,需要在build_config.rb里添加对应的gem。 - 两种方案都不需要写C扩展,纯Ruby代码就能实现。
内容的提问来源于stack exchange,提问作者Rick
相关产品推荐
相关产品推荐

