2016-03-01 108 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

我已經向上投票了,但不幸的是我沒有足夠的聲望讓它公開顯示。雖然我錯過了選中標記,但我現在已經檢查過了。 –