我如何在 Ruby 的陷阱上下文中使用 Redis 调用?

How could I use Redis calls in trap context in Ruby?

我的脚本从 redis 数据库中获取元素,最后用它做一些工作。我需要确保如果脚本以 ^C 或其他信号结束,该元素将返回到数据库中。 我正在努力

require "redis"

class Test
  attr_reader :quit

  def initialize
    @redis = Redis.new
  end

  def trap_signal
    trap("INT") {
      puts "get ready to exit"
      @redis.rpush "TestQueue", @elem # I need to be sure that @emelent always puts back in the database 
      @quit = true}
  end

  def run!
    trap_signal
    @elem = "test string"
    @redis.rpush "TestQueue", @elem
    while !quit
      @redis.blpop "TestQueue", @elem
      # Do some work whith @element
      sleep 1
      # And put it back in the database 
      @redis.rpush "TestQueue", @elem
    end
  end

end

Test.new.run!

但是出现这个错误

^Cget ready to exit
/usr/lib/ruby/2.1.0/monitor.rb:185:in `lock': can't be called from trap context (ThreadError)
    from /usr/lib/ruby/2.1.0/monitor.rb:185:in `mon_enter'
    from /usr/lib/ruby/2.1.0/monitor.rb:209:in `mon_synchronize'
    from /home/kusayu/.gem/ruby/2.1.0/gems/redis-3.2.0/lib/redis.rb:37:in `synchronize'
    from /home/kusayu/.gem/ruby/2.1.0/gems/redis-3.2.0/lib/redis.rb:991:in `rpush'
    from test.rb:13:in `block in trap_signal'
    from test.rb:24:in `call'
    from test.rb:24:in `sleep'
    from test.rb:24:in `run!'
    from test.rb:32:in `<main>'

您的代码已经可以正常工作,只需从信号处理程序中删除 @redis.rpush

你不应该在信号处理程序中进行 运行 "heavy" 操作(无论如何你都会因此得到一个异常)。最好使用 @quit = true 之类的变量来向主循环发出结束时间的信号,然后让主循环处理适当的清理工作。

因此,如果您只是从 INT 信号处理程序中删除 @redis.rpush,那么您将确保该元素返回到数据库中,因为主循环只会在 @quittrue.