第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Ruby 的多線程

本章節(jié)讓我們來學習 Ruby 的多線程。您將會了解到:什么是多線程,Ruby 中如何創(chuàng)建線程等知識。

1. Ruby 中的線程

通俗一點來講,線程可以讓程序同時執(zhí)行多項操作。

比如:讀取多個文件、處理多個請求、建立多個API連接。多線程可以更好地利用CPU的核心,CPU的一個核好比一個普通人,一個普通人只能干一件事,多個人可以分開干不同的事或干很多次同樣的事。

注意事項:

在MRI(Matz 的 Ruby 解釋器)中,這是運行 Ruby 應用程序的默認方式,只有在運行 I/O 綁定的應用程序時,您才能從線程中受益。由于存在 GIL(Global Interpreter Lock,是由編程語言解釋器線程持有的互斥鎖,以避免與其他線程共享不是線程安全的代碼。),因此存在此限制。對于一般的 Ruby 和 Python 應用,即使在多核處理器上運行,使用 GIL 的解釋器始終總是允許一次僅執(zhí)行一個線程。

每個進程都有至少一個線程,您可以按需創(chuàng)建更多線程。

2. I/O 綁定應用程序

首先,我們需要討論 CPU 綁定和 I/O 綁定應用程序之間的區(qū)別。

I/O 綁定應用程序是需要等待外部資源的應用程序:

  • API請求;
  • 數(shù)據庫(查詢結果);
  • 磁盤讀取。

線程可以在等待資源可用時決定停止。

這意味著另一個線程可以運行并執(zhí)行其任務,而不會浪費時間等待。

I/O 綁定應用程序的一個示例是 Web 爬蟲(crawler)。

對于每個請求,爬蟲都必須等待服務器響應,并且在等待時它什么也不能做。

您可以一次發(fā)出4個請求,并在它們返回時處理響應,這將使您更快地獲取頁面。

2.1 創(chuàng)建一個線程

您可以通過調用Thread.new創(chuàng)建一個新的Ruby線程。確保傳遞帶有該線程需要運行的代碼的塊。

實例:

Thread.new { puts "hello from thread" }

# ---- 輸出結果 ----

是不是很簡單。

但你會發(fā)現(xiàn),線程沒有輸出內容,這是因為Ruby 不等待線程完成。

您需要在線程上調用join方法來修復上面的代碼。

實例:

Thread.new { puts "hello from thread" }.join

# ---- 輸出結果 ----
hello from thread

如果要創(chuàng)建多個線程,可以將它們放入數(shù)組中,并在每個線程上調用join。

實例:

Thread.new { puts "hello from thread1" }.join
Thread.new { puts "hello from thread2" }.join
Thread.new { puts "hello from thread3" }.join

# ---- 輸出結果 ----
hello from thread1
hello from thread2
hello from thread3

學習Ruby的線程時,我們要多參考 Ruby 線程的文檔。

2.2 線程與異常

如果線程內發(fā)生異常,它將在不停止程序或不顯示任何錯誤消息的情況下靜默死。

實例:

Thread.new { raise 'hell' }

# ---- 輸出結果 ----

為了進行調試,您可能希望程序在發(fā)生不良情況時停止運行。

為此,您可以將 Thread 上的以下標志設置為 true:

Thread.abort_on_exception = true

在創(chuàng)建線程之前,請確保設置此標志。

實例:

Thread.abort_on_exception = true
Thread.new { raise 'hell' }
sleep(1)
# ---- 輸出結果 ----
ruby.rb:2:in `block in <main>': hell (RuntimeError)

**注意事項:**這里需要增加sleep(1),否則不會拋出異常。

2.3 線程池

假設您要處理數(shù)百個項目,為每個項目啟動一個線程將破壞您的系統(tǒng)資源。

它看起來像這樣:

pages_to_crawl = %w( index about contact ... )
pages_to_crawl.each do |page|
  Thread.new { puts page }
end

如果這樣做,您將與服務器啟動數(shù)百個連接,因此這可能不是一個好主意。

一種解決方案是使用線程池。線程池使您可以在任何給定時間控制活動線程的數(shù)量。

您可以建立自己的池,但是我不建議你這樣去做,Ruby有一個Gem可以為您完成這個操作。

實例:

require 'celluloid'
class Worker
  include Celluloid
  def process_page(url)
    puts url
  end
end
pages_to_crawl = %w( index about contact products ... )
worker_pool    = Worker.pool(size: 5)
# If you need to collect the return values check out 'futures'
pages_to_crawl.each do |page|
   worker_pool.process_page(page)
end

這次只有5個線程在運行,完成后他們將選擇下一個項目。

2.4 資源競爭風險

您必須知道并發(fā)代碼存在一些問題,例如,線程容易出現(xiàn)資源競爭狀況,比如同一時刻操縱了一個變量。競爭條件是當事情發(fā)生混亂并弄亂時。

另一個問題是死鎖deadlock)這是當一個線程擁有對某個資源的獨占訪問權(使用互斥鎖(mutex)之類的鎖定系統(tǒng))而從未釋放它時,這使得所有其他線程都無法訪問它。

為避免這些問題,最好避免使用原始線程,并堅持使用一些已經為您處理好細節(jié)的Gem。

3. 小結

本章節(jié)中我們學習到了如何使用 Ruby 來創(chuàng)建一個線程。如何讓創(chuàng)建的線程拋出異常,線程池是什么,線程中存在的風險有什么。