2016-03-01 109 views
1

我需要一个线程来停止自己,然后被另一个线程唤醒。我遇到的问题是我找不到一个完全万无一失的好解决方案。我的代码现在看起来是这样的:安全地唤醒Ruby中的线程

def initialize 
    @data_lock = Mutex.new 
    @closed = false 
end 

def get_response 
    @data_lock.synchronize do 
    @blocked_thread = Thread.current 
    end 
    # This loop is a safe guard against accidental wakeup of thread 
    loop do 
    @data_lock.synchronize do 
     if @closed 
     return @response 
     end 
    end 
    # FIXME: If context switch happens here the thread will be permanently frozen. 
    Thread.stop # Stop current thread and wait for call to close() 
    end 
end 


def close(response) 
    @data_lock.synchronize do 
    @closed = true 
    @response = response 
    Thread.pass # An attempt at minimizing the risk of permanently freezing threads 
    if @blocked_thread.is_a? Thread and @blocked_thread.status == 'sleep' 
     @blocked_thread.wakeup 
    end 
    end 
end 

它应该工作的方式是调用get_response将阻止当前线程,当另一个线程调用close()的第一个线程应该被唤醒并返回通过@response发送的值。

这应该适用于所有情况,除非第二个线程在第一个线程停止并且在第一个线程停止之前有一个上下文切换的情况下会关闭。我怎样才能删除这个(不太可能)的可能性?

回答

0

与线程通信的最简单方法是使用Thread#Queue对象。线程#队列是一个线程安全的FIFO队列。

require "thread" 

@queue = Queue.new 

当胎面想要阻塞直到发出信号时,它从队列中读取。该线程将停止,而队列为空:

@queue.deq 

要唤醒线程,写东西到队列:

@queue.enq :wakeup 

在这里,我们只是把一个符号插入到队列中。但是你也可以写入你希望线程处理的队列。例如,如果一个线程正在处理的URL,它可以从队列中检索它们:

loop do 
    url = @queue.deq 
    # process the url 
end 

还有一些其他线程可以将URL添加到队列:

@queue.enq "http://stackoverflow.com" 
@queue.enq "http://meta.stackoverflow.com" 
+0

感谢,这正是我需要的! –

+0

@AndreasHagelberg我很高兴它是有帮助的!如果你愿意,你可以点击向上三角形来投票(这是我们在这里展示的欣赏)。还有一个复选标记,您可以点击以显示这是帮助您的最佳答案。 –

+0

我已经向上投票了,但不幸的是我没有足够的声望让它公开显示。虽然我错过了选中标记,但我现在已经检查过了。 –