[ruby-list:50593] Re: Fwd: Re: Re: [質問]thread内sleepコール 他threadよりsleep状態からrun状態にされた際、指定時間sleepさせるには?

From: <yamataka@...08.itscom.net>
Date: 2017-10-04 07:18:03 UTC
List: ruby-list #50593
> こんにちは、市田です。

市田さん

山口です。

いつもご教示ありがとうございます。

> On 2017/09/29 19:38, yamataka@u08.itscom.net wrote:

>> 但し、exec_thread 再開後は、停止した際から、sleep設定時間の残り時間を
>> exec_threadは実行というような感じにしたいのです。

>スレッドが stop から run に状態変化する際、sleep の途中からというのは
>ないのではと思います。

そうでしたか。

> exec_thread = Thread.new do
>   loop do
>     puts "in exec_thread"
>     n = 10 # sleep 100s
>     puts "in exec_thread sleep start #{n}s"
>     start = Time.now
>     while n > 0
>       sleep(0.1)
>       n -= 0.1
>     end
>     puts "in exec_thread sleep end %.1f" % (Time.now - start)
>   end
>end

サンプルコード提示ありがとうございます。


>「刻み」をどうするかは、どれくらい sleep するのかと、期待する時間精度
> によって変わるでしょう。

時間精度は、厳密に正確でなくて困らず

> また、元々のsleep時間以上、停止させられていた場合でも、「継続」するの
> が良いかどうかは考慮要と思います。

継続するというような感じて、結局、input_thread から Queue を用いて、
exec_thread 側で、その命令に従って動くようなコードにしました。

exec_threadでは、sleepシステムコール待ち時間全て指定するのではなく1s単位
で実行、その回数を管理。
その実行中に、input_threadより、pause 命令がくると、sleep実行を行わない。
再度、pause命令がきたら、残りの sleep(1) の回数を実行する。

というような以下の様なコードになりました。

# もっとこう書いた方が、ruby らしいとかあれば、コメント頂けると幸いです。

require 'pp'

q = Queue.new

def exec_run
  puts "[INFO] #{__method__} start"
end

def exec_pause
  puts "[INFO] #{__method__} pause"
end

def exec_nil
  puts "[INFO] #{__method__} do nothing"
end

exec_thread = Thread.new do

  # sd: state diagram
  sd = [ 
    # ets      etc     call
    [{:run   => :pause}, :exec_pause],  
    [{:run   => :run  }, :exec_nil],
    [{:pause => :pause}, :exec_nil],
    [{:pause => :run  }, :exec_run]
  ]

  puts "in exec_thread"

  ets  = :run  # ets: exec thread status
  sc   = 30 # sleep count
  rseq = :run1 # rseq: run sequence

  loop do
    begin
      etc = q.pop(non_block = true) # etc : exec thread control

      func = nil

      sd.each do |sdc|
        f = false
        match = sdc.first.select { |k,v| k == ets && v == etc }
        unless match.empty?
          ets = match.to_a.first[1]
          func = sdc[1]
          f = true
        end
        break if f
      end
      self.send(func)
    rescue => e
      case ets
      when :run
        case rseq
        when :run1
          puts "start run1"
          rseq = :runs
        when :runs
          sc = 20
          rseq = :runsc
        when :runsc
          puts "sleep #{sc}s"          
          sleep(1)
          sc -= 1
          rseq = :run1 if sc < 0
        else
          puts "[ERROR] #{rseq}"
          exit
        end
      when :pause
      else # do nothing
      end
    end
  end
end

input_thread = Thread.new do
  ets = :run # ets: execute thread status

  loop do
    ret = STDIN.gets

    case ret
    when /^ *q *$/
      puts "get q"
      exit
    when /^ *p *$/    
      puts "get p"
      if ets == :run
        ets = :pause
        q.push(:pause)
      else
        ets = :run
        q.push(:run)
      end
    end
  end
end

exec_thread.join
input_thread.join



In This Thread