So sánh việc sử dụng select() và fork()
Trong các bàitrước, chúng ta đã được học về một thành phầnrất quan trọng củahệ điều hành là tiến trình vàcách hệ điều hành Linux tạo ra cũng nhưquản lý tiến trình. Trong chương này, chúng ta sẽ tìm hiểu một thành phầnquan trọng tiếp theo và có liên quan rất chặt chẽ với tiến trình là luồng. Luồng với tên tiếng anh là Thread được sử dụng rất nhiều trong lập trình Linux nhằmthực hiện đa nhiệm cho tiến trình và tối ưu hóa performance của hệ thống. Vì thread là thuật ngữ đã quá phổ biến và xuất hiện trong tất cả các cuốn sách và các websitevề Linux, nên từ phần sau của khóa họcchúng tasẽ dùng thuật ngữ thread thay cho luồng.
Hãy cùng bắt đầu bằng việctìm hiểu tổng quanvề cách hoạt động của thread, cách một thread được tạo ra cũng như kết thúc, từ đó có cái nhìn tổng thểkhi nào thì nên dùng và không nên dùng thread. Tiếp theo, chúng ta sẽ làm quen vớicác hàm API của thư viện Posix thread [pthread],mộtthư viện tiêu chuẩn cho lập trìnhthread được sử dụng thống trị trong lập trìnhLinux.
Why asynchronous non-blocking I/O??
Chi tiết ở The C10K problem
Ngắn dọn là do OS Thread quá tốn kém [memory, CPU time context switching]. Các mô hình blocking với 1 thread / 1 request trở nên không còn hiệu quả.
Ví dụ trên cho thấy làm thế nào để chạy mã không đồng bộ bằng cách thực thi EM.system [hoạt động I/O] và chạy một block như một callback cái mà sẽ được thực hiện khi lệnh hệ thống đã hoàn tất. Ưu điểm:
- Có thể đạt được hiệu suất tuyệt vời cho các ứng dụng được nối mạng chậm như máy chủ web và proxy với một luồng duy nhất.
- Nó cho phép bạn tránh các chương trình đa luồng phức tạp, những bất lợi được mô tả ở trên.
Nhược điểm:
- Mỗi toán tử I/O nên được hỗ trợ EM không đồng bộ. Điều này có nghĩa là bạn nên sử dụng các phiên bản cụ thể của hệ thống, DB adapter, HTTP client ... có thể dẫn đến các phiên bản vá lỗi monkey-patched, thiếu hỗ trợ và các tùy chọn hạn chế.
- Công việc thực hiện trong luồng chính cho mỗi vòng lặp nên nhỏ. Ngoài ra, có thể sử dụng Defer, nó thực hiện mã trong các luồng riêng biệt từ thread pool, tuy nhiên, nó có thể dẫn đến các vấn đề đa luồng thảo luận trước đó.
- Khó để thực hiện các hệ thống phức tạp vì các lỗi xử lý và callbacks. Callback Hell cũng có thể có trong Ruby, nhưng nó có thể được ngăn chặn bằng Fibres.
- EventMachine chính là một sự phụ thuộc rất lớn: 17K LOC [dòng mã] trong Ruby và 10K LOC trong C ++.
Ví dụ:
- Goliath - một máy chủ không đồng bộ đơn luồng.
- AMQP - RabbitMQ client. Tuy nhiên, người tạo ra gem này đề xuất sử dụng non-EM-based phiên bản Bunny. Lưu ý rằng các công cụ di chuyển sang cài đặt EM-less là một xu hướng chung. Ví dụ: người sáng tạo ActionCable đã quyết định sử dụng nio4r cấp thấp, người sáng tạo sinatra-synchrony viết lại nó với Celluloid, v.v.
Fibers
Fibers là các primitives nhẹ trong thư viện chuẩn Ruby, có thể được tạm dừng, tiếp tục và lập lịch bằng tay. Chúng khá giống với ES6 Generator nếu bạn đã quen thuộc với JavaScript. Có thể chạy hàng chục nghìn Fibers trong một luồng đơn. Thông thường, Fibers được sử dụng với EventMachine để tránh callback và làm cho code nhìn đồng bộ. Vì vậy, đoạn code sau đây:
EventMachine.run do page = EM::HttpRequest.new['//google.ca/'].get page.errback { puts "Google is down" } page.callback { url = '//google.ca/search?q=universe.com' about = EM::HttpRequest.new[url].get about.errback { ... } about.callback { ... } } endCó thể được viết lại như sau:
EventMachine.run do Fiber.new { page = http_get['//www.google.com/'] if page.response_header.status == 200 about = http_get['//google.ca/search?q=universe.com'] # ... else puts "Google is down" end }.resume end def http_get[url] current_fiber = Fiber.current http = EM::HttpRequest.new[url].get http.callback { current_fiber.resume[http] } http.errback { current_fiber.resume[http] } Fiber.yield endVì vậy, về cơ bản, Fiber#yield trở lại ngữ cảnh đã khôi phục lại Fiber và trả về giá trị đã được truyền cho Fiber#resume. Ưu điểm:
- Fibers cho phép bạn đơn giản hóa mã không đồng bộ bằng cách thay thế các callbacks lồng nhau.
Nhược điểm:
- Không thực sự giải quyết các vấn đề concurrency.
- Chúng ít khi được sử dụng trực tiếp trong code cấp ứng dụng.
Ví dụ:
- Em-synchrony - một thư viện, được viết bởi Ilya Grigorik, một kỹ sư tại Google, giúp tích hợp EventMachine với Fibres cho các client khác nhau như MySQL2, Mongo, Memcached ...
Kết luận
Hãy chọn một mô hình concurrency tùy thuộc vào nhu cầu của bạn. Ví dụ, cần phải chạy CPU và bộ nhớ mã chuyên sâu và có đủ nguồn lực - sử dụng processes. Phải thực hiện nhiều hoạt động I/O như yêu cầu HTTP - sử dụng threads. Cần phải mở rộng quy mô tối đa - sử dụng EventMachine.
Tham khảo: //engineering.universe.com/introduction-to-concurrency-models-with-ruby-part-i-550d0dbb970