用信号重新启动循环

时间:2014-02-20 02:41:28

标签: ruby loops signals

假设我有一个带有如下循环的程序:

#!/usr/bin/env ruby

puts $$

def do_work
   puts Time.now
end

Signal.trap("HUP") do
  do_work
end

loop do
  sleep 60
  do_work
end

它每60秒“有效”。有时我不想等待整整60秒,所以我立即将HUP信号捕获到do_work。这很好用,除了一件事:我想在处理信号后重置60秒钟。它现在设置的方式,如果睡眠是@ 45秒并且我发送程序SIGHUP,它将do_work然后15秒后再次do_work

如何使用上述约束对其进行重新设计?

2 个答案:

答案 0 :(得分:1)

免责声明:我使用信号INT(由ctrl + C触发)测试我的代码,因为我在Windows上没有HUP。 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals让我相信HUP也应该正常工作,但我不能百分百肯定。

对于您列出的示例代码,您可以将sleep 60从循环移动到do_work方法内部:

def do_work
  puts Time.now
  sleep 60
end

Signal.trap("HUP") do
  do_work
end

loop do
  do_work
end

但是,可能有更好的方法来封装逻辑并使其可重复使用。以下是我提出的建议:

class Worker
  attr_accessor :interval
  attr_reader :times_worked

  def initialize(interval, signal)
    @signal = signal
    @interval = interval
    @times_worked = 0
  end

  def set_task(&block)
    @work_to_do = block
    Signal.trap(signal) { do_work }
    start
  end

  def stop
    @stop = true
  end

  def start
    @stop = false
    loop do
      break if @stop
      do_work
    end
  end

  def do_work
    @work_to_do.call
    @times_worked += 1 # just an example of something you could put here
    sleep @interval
  end
end

worker = Worker.new(5, "HUP")
worker.set_task { puts Time.now }

答案 1 :(得分:1)

可能的解决方案:

q1, q2 = (0..1).map { Queue.new }

Thread.new do
  loop do
    time, seq = q1.pop
    delta = time - Time.now
    sleep delta if delta > 0
    q2.push seq
  end
end

Signal.trap("HUP") do
  Thread.new do
    q2.push nil
  end
end

seq = 0
q2.push seq
loop do
  # Drop expired sequences
  while (event_seq = q2.pop; event_seq && event_seq != seq)
  end

  # If HUP expire current sequence
  seq += 1 unless event_seq

  # Do work here
  p Time.now

  q1.push [Time.now + 60, seq]
end