アイデアは、入力がコンソールによって提供され、入力の最初の単語として一意の「id」で識別されるということです。新しい ID が検出されると、新しいスレッドが生成され、その後の入力は「start」になります。同じ ID を持つ入力が「閉じる」と言うと、スレッドは閉じる必要があります。ステートメントの順序はランダムです。
1 に答える
ここでの秘訣は、すべての読み取りを単一のスレッド (最も簡単にはメイン スレッド) で行うことです。そのスレッドは、コマンドを解析し、スレッドを開始または停止し、コマンドをスレッドにディスパッチします。各スレッドには独自の 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"