2015-09-26 115 views
0

所以我有一個單獨的線程運行的記者方法,它看起來有點像這樣紅寶石重複線程

def report 
    @reporters.each do |reporter| 
    Thread.new{ reporter.report } 
    end 
end 

的目的是,記者的對象不與我的主要的干擾性能代碼循環。然而,這確實創造了很多新線程,這似乎佔用了大量資源。

我想反而收集這些新線程,並在調用報告方法時重新運行它們。或者在每次調用記錄對象時向單線程發送報告方法。

有沒有一種方法可以在初始化後在一個單獨的線程中調用方法?

回答

1

雖然我與@AlexeyShein一起認爲測試過的庫是更好的選擇,但請允許我提出一些想法/問題以及提供基本的線程池演示。

我提供的代碼類似於我爲Iodine項目編寫的代碼,雖然更簡單並且稍微優化一點。在你的線程池

  1. 任務可能無法完成

    當主機OS /服務器關閉應用程序下來,它可能不會等待所有的任務來完成。

    在決定您的應用程序有足夠的時間並強行殺死該進程之前,託管OS /服務器通常會等待一段時間(在Heroku上可能長達30秒)。

    這是正常和預期的行爲。

  2. 任務不會持久

    每當你的應用程序重新啓動,任務隊列將是一個清新干淨的隊列。

    要解決此問題,通常需要做兩件事:1.將任務保存到數據庫/模塊中;和2.在不同的進程中運行後臺任務管理器(以在縮放應用程序時避免競爭狀況)。

如果做有關機後重新開始未完成的任務不這一點,這裏是一個比較簡單的線程池的解決方案。

下面的測試代碼應該告訴你即使是小任務可能會導致rundown在退出被調用後仍然存在。如果你的應用程序會持續很長時間,它可能會被你的主機強行關閉。

class ThreadPool 
    def initialize number_of_threads 
     @queue = Queue.new 
     @stop = false 
     @threads = [] 
     number_of_threads.times do 
      @threads << Thread.new { work until @stop } 
     end 
     Kernel.at_exit { rundown } 
    end 

    def run &block 
     return false unless block 
     @queue << block 
     block 
    end 

    protected 

    def work 
     begin 
      @queue.pop.call 
     rescue => e 
      warn "A background task failed with #{e.message}" 
     end 
    end 

    def rundown 
     sleep 0.1 until @queue.empty? 
     stop 
     join 
     work until @queue.empty? 
    end 

    # will wait for all threads to finish 
    # if stop isn't called, this will hang forever! 
    def join 
     @threads.each {|t| t.join rescue true } 
    end 

    # will stop the thread pool and let the threads exit normally 
    def stop 
     @stop = true 
     (@threads.count * 2).times { run { nil } } 
    end 
end 



BK = ThreadPool.new 12 
100.times {|i| BK.run {sleep 0.2; puts i.to_s} } 

exit 
+0

糟糕,我在'stop'方法有一個輕微的錯字,因爲我仍然使用它的舊名稱('defer')來調用'run'方法。它現在是固定的,所以沒有任何錯誤應顯示線程池來完成破敗後。 – Myst

1

該設計模式被稱爲線程池。互聯網上有大量有關Ruby線程池實現的信息。

0

多線程技術可能非常棘手,所以我認爲在你的情況下最好的選擇是使用像sidekiq這樣經過戰鬥測試的庫,它可以承擔所有的繁重工作。