Computer >> Máy Tính >  >> Lập trình >> Ruby

Cách sử dụng Ruby Threads:Hướng dẫn dễ hiểu

Một chuỗi trong Ruby là gì?

Các chuỗi làm cho các chương trình Ruby của bạn thực hiện nhiều việc cùng một lúc.

Những thứ như :

  • Đọc nhiều tệp
  • Xử lý nhiều yêu cầu web
  • Tạo nhiều kết nối API

Kết quả của việc sử dụng các luồng, bạn sẽ có một chương trình Ruby đa luồng, chương trình này có thể hoàn thành công việc nhanh hơn.

Nhưng một cảnh báo…

Trong MRI (Matz’s Ruby Interpreter), cách mặc định để chạy các ứng dụng Ruby, bạn sẽ chỉ được hưởng lợi từ các luồng khi chạy các ứng dụng i / o ràng buộc .

Hạn chế này tồn tại do GIL (Khóa thông dịch viên toàn cầu) .

Các trình thông dịch Ruby thay thế như JRuby hoặc Rubinius tận dụng tối đa khả năng đa luồng.

Cách sử dụng Ruby Threads:Hướng dẫn dễ hiểu

Vậy, chuỗi là gì?

Chủ đề là công nhân hoặc đơn vị thực thi.

Mọi quy trình đều có ít nhất một chuỗi và bạn có thể tạo thêm theo yêu cầu.

Tôi biết bạn muốn xem một ví dụ về mã.

Nhưng trước tiên, chúng ta cần nói về sự khác biệt giữa các ứng dụng ràng buộc CPU và các ứng dụng ràng buộc I / O.

Ứng dụng ràng buộc I / O

Một ứng dụng liên kết i / o là một trong những cần phải đợi một tài nguyên bên ngoài:

  • một yêu cầu API
  • cơ sở dữ liệu (kết quả truy vấn)
  • một đĩa đã đọc

Một luồng có thể quyết định dừng trong khi nó chờ một tài nguyên có sẵn. Điều này có nghĩa là một luồng khác có thể chạy và thực hiện công việc của nó và không mất thời gian chờ đợi.

Một ví dụ về ứng dụng liên kết i / o là một trình thu thập thông tin web.

Đối với mọi yêu cầu, trình thu thập thông tin phải đợi máy chủ phản hồi và nó không thể làm bất cứ điều gì trong khi chờ đợi.

Nhưng nếu bạn đang sử dụng chuỗi…

Bạn có thể đưa ra 4 yêu cầu cùng một lúc và xử lý phản hồi khi chúng quay lại, điều này sẽ cho phép bạn tìm nạp trang nhanh hơn.

Bây giờ là lúc cho ví dụ mã của bạn.

Tạo chuỗi Ruby

Bạn có thể tạo một chuỗi Ruby mới bằng cách gọi Thread.new .

Đảm bảo chuyển vào một khối có mã mà chuỗi này cần để chạy.

Thread.new { puts "hello from thread" }

Khá dễ dàng, phải không?

Tuy nhiên.

Nếu bạn có mã sau, bạn sẽ nhận thấy rằng không có đầu ra từ chuỗi:

t = Thread.new { puts 10**10 }
puts "hello"

Vấn đề là Ruby không đợi các chuỗi kết thúc.

Bạn cần gọi join trên chuỗi của bạn để sửa mã ở trên:

t = Thread.new { puts 10**10 }
puts "hello"
t.join

Nếu bạn muốn tạo nhiều chuỗi, bạn có thể đặt chúng bên trong một mảng và gọi join trên mọi chủ đề.

Ví dụ :

threads = []

10.times {
  threads << Thread.new { puts 1 }
}

threads.each(&:join)

Trong quá trình khám phá các chuỗi Ruby, bạn có thể thấy tài liệu hữu ích:

https://ruby-doc.org/core-2.5.0/Thread.html

Chủ đề và ngoại lệ

Nếu một ngoại lệ xảy ra bên trong một chuỗi, nó sẽ chết lặng lẽ mà không dừng chương trình của bạn hoặc hiển thị bất kỳ loại thông báo lỗi nào.

Đây là một ví dụ:

Thread.new { raise 'hell' }

Đối với mục đích gỡ lỗi, bạn có thể muốn chương trình của mình dừng lại khi có điều gì đó xấu xảy ra. Để làm điều đó, bạn có thể đặt cờ sau trên Thread thành true:

Thread.abort_on_exception = true

Đảm bảo đặt cờ này trước khi bạn tạo chuỗi của mình 🙂

Hồ bơi chủ đề

Giả sử bạn có hàng trăm mục cần xử lý, việc bắt đầu một chuỗi cho mỗi mục sẽ phá hủy tài nguyên hệ thống của bạn.

Nó sẽ trông giống như thế này:

pages_to_crawl = %w( index about contact ... )

pages_to_crawl.each do |page|
  Thread.new { puts page }
end

Nếu bạn làm điều này, bạn sẽ khởi chạy hàng trăm kết nối với máy chủ, vì vậy đó có lẽ không phải là ý kiến ​​hay.

Một giải pháp là sử dụng nhóm luồng.

Nhóm chuỗi cho phép bạn kiểm soát số lượng chuỗi đang hoạt động tại bất kỳ thời điểm nào.

Bạn có thể xây dựng hồ bơi của riêng mình, nhưng tôi không khuyên bạn nên làm như vậy. Trong ví dụ sau, chúng tôi đang sử dụng đá quý celluloid để thực hiện việc này cho bạn.

Lưu ý:Celluloid hiện không còn nguyên bản, nhưng ý tưởng chung về nhóm công nhân vẫn được áp dụng.

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

Lần này sẽ chỉ có 5 luồng chạy và khi kết thúc, họ sẽ chọn mục tiếp theo.

Điều kiện cuộc đua và các mối nguy hiểm khác

Điều này nghe có vẻ rất thú vị nhưng trước khi bắt đầu rắc các chủ đề lên toàn bộ mã của mình, bạn phải biết rằng có một số vấn đề liên quan đến mã đồng thời.

Ví dụ:các chủ đề có xu hướng gặp phải các điều kiện về chủng tộc.

Đ iều kiện cuộc đua là khi mọi thứ diễn ra không theo thứ tự và trở nên lộn xộn.

Một vấn đề khác có thể xảy ra là bế tắc. Đây là khi một luồng giữ quyền truy cập độc quyền (sử dụng hệ thống khóa như mutex) vào một số tài nguyên và không bao giờ giải phóng tài nguyên đó, điều này khiến tất cả các luồng khác không thể truy cập được.

Để tránh những vấn đề này, tốt nhất bạn nên tránh các chủ đề thô và gắn bó với một số viên ngọc đã chăm sóc các chi tiết cho bạn.

Thêm chuỗi ngọc

Chúng tôi đã sử dụng celluloid cho nhóm chủ đề của mình, nhưng có nhiều đá quý khác tập trung vào đồng tiền mà bạn nên xem:

  • https://github.com/grosser/parallel
  • https://github.com/chadrem/workers
  • https://github.com/ruby-concurrency/concurrent-ruby

Vậy là xong, hy vọng bạn đã học được một hoặc hai điều về chuỗi Ruby !

Nếu bạn thấy bài viết này hữu ích, hãy chia sẻ nó với bạn bè của bạn để họ cũng có thể học hỏi 🙂