2

アイデアは、入力がコンソールによって提供され、入力の最初の単語として一意の「id」で識別されるということです。新しい ID が検出されると、新しいスレッドが生成され、その後の入力は「start」になります。同じ ID を持つ入力が「閉じる」と言うと、スレッドは閉じる必要があります。ステートメントの順序はランダムです。

4

1 に答える 1

3

ここでの秘訣は、すべての読み取りを単一のスレッド (最も簡単にはメイン スレッド) で行うことです。そのスレッドは、コマンドを解析し、スレッドを開始または停止し、コマンドをスレッドにディスパッチします。各スレッドには独自の Queue があり、そこからコマンドが読み取られ、メイン スレッドがコマンドを入れます。それがどのように行われるか見てみましょう。

DRY になるように、制御コマンド用の小さなモジュールから始めます。

module ControlCommands
  START_COMMAND = 'start'
  STOP_COMMAND = 'stop'
end

それでは、「メイン」を見てみましょう。

class Main

  def initialize
    @workers = Workers.new
    @console = Console.new(@workers)
  end

  def run
    @console.read_and_dispatch
    @workers.join
  end

end

Main.new.run

ここでは何も起こっていません。コンソールを作成し、コマンドを読み取ってワーカーにディスパッチするように指示します。コンソールは、入力がなくなるまで戻りません。@workers.joinプログラムが終了する前に、すべてのワーカーが作業を終了し、適切にシャットダウンしたことを確認するための呼び出し。

コンソール クラスは次のとおりです。

class Console

  def initialize(workers)
    @workers = workers
  end

  def read_and_dispatch
    while input = gets
      @workers.dispatch *parse_input(input)
    end
  end

  private

  def parse_input(input)
    input.match(/^(\w+) *(.*)$/)[1..2]
  end

end

read_and_dispatchメインループです。それが担当するのは、入力の読み取りと解析だけです。入力がある限り、それをワーカー名とコマンドに分割し、コマンドを処理するようにワーカーに指示します。

ワーカークラスは次のとおりです。

class Workers

  include ControlCommands

  def initialize
    @workers = {}
  end

  def dispatch(worker_name, command)
    case command
    when START_COMMAND
      start_worker worker_name
    when STOP_COMMAND
      stop_worker worker_name
    else
      dispatch_worker worker_name, command
    end
  end

  def join
    @workers.each do |worker_name, worker|
      worker.stop
      worker.join
    end
  end

  private

  def start_worker(worker_name)
    @workers[worker_name] = Worker.new(worker_name)
  end

  def stop_worker(worker_name)
    @workers[worker_name].stop
  end

  def dispatch_worker(worker_name, command)
    @workers[worker_name].dispatch command
  end

end

肉のほとんどがここにある。このクラスはワーカー (スレッド) を作成し、停止し、コマンドをディスパッチします。ここにはエラー処理がないことに注意してください。開始されていないスレッドを停止しようとしたり、既に開始されているスレッドを開始したり、存在しないスレッドにコマンドをディスパッチしようとすると、プログラムがクラッシュしたり、誤動作したりします。これらの状況の取り扱いは、「読者の演習」として残しておきます。

ワーカークラスは次のとおりです。

class Worker

  include ControlCommands

  def initialize(name)
    @name = name
    @commands = Queue.new
    @thread = start_thread
  end

  def dispatch(command)
    @commands.enq command
  end

  def stop
    @commands.enq STOP_COMMAND
  end

  def join
    @thread.join
  end

  private

  def start_thread
    Thread.new do
      loop do
        command = @commands.deq
        break if command == STOP_COMMAND
        process_command command
      end
    end
  end

  def process_command(command)
    print "thread #{@name} received command #{command.inspect}\n"
  end

end

このクラスには、スレッドと、メイン スレッド (コンソールを読み取るスレッド) とワーカー スレッド間の通信に使用されるキューが含まれます。そのキューは、スレッドが終了して応答するキューに STOP_COMMAND を入れることにより、スレッドを同期的に停止するためにも使用されます。余裕がある場合は、非同期ではなく同期的にスレッドを停止することをお勧めします。

簡単な入力ファイルを次に示します。

foo start
bar start
foo input
bar input
foo stop
bar stop

そして、プログラムにその入力が提示されたときの出力:

thread bar received command "input"
thread foo received command "input"
于 2012-12-12T14:58:46.120 に答える