这个想法是输入将由控制台提供,并将使用唯一的“id”作为输入的第一个单词来标识。当遇到新的 id 时会产生一个新线程,随后的输入是“开始”。当具有相同 id 的输入会说“关闭”时,线程应该关闭。语句的顺序是随机的。
1 回答
这里的诀窍是让一个线程(最容易的是主线程)完成所有读取。该线程将解析命令,启动或停止线程,并将命令分派给线程。每个线程都有自己的队列,它从中读取命令,主线程将命令放入其中。让我们看看它是如何完成的。
我们将从控制命令的一个小模块开始,以便它们是 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"