So sánh việc sử dụng select() và fork()
Thread và Posix ThreadTrong 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. Show 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:
Nhược điểm:
Ví dụ:
FibersFibers 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('https://google.ca/').get page.errback { puts "Google is down" } page.callback { url = 'https://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('http://www.google.com/') if page.response_header.status == 200 about = http_get('https://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:
Nhược điểm:
Ví dụ:
Kết luậnHã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: https://engineering.universe.com/introduction-to-concurrency-models-with-ruby-part-i-550d0dbb970 Understand Nodejs
Nodejs hiện nay đang rất nổi như một xu thế công nghệ mới. Với sự mạnh mẽ, cấu trúc khác biệt nên Nodejs đã tạo nên một cơn sốt thời gian qua: nhanh, tốn ít tài nguyên, đáp ứng được lượng request lớn. Đặc biệt nó đáp ứng được tính realtime của ứng dụng. Mình là một người khá tò mò và thích khám phá điều mới lạ. Lúc đầu đọc qua tài liệu và ví dụ với các cụm từ event-driven, non-blocking, asynchonous, single thread... mình bị choáng. Vì các khái niệm này quá trìu tượng và khó hiểu. Đó là động lực giúp mình đi tìm câu trả lời? Điêu gì tạo nên sức mạnh của Node.js. I/OI/O là quá trình giao tiếp (lấy dữ liệu vào, trả dữ liệu ra) giữa một hệ thống thông tin và môi trường bên ngoài. Với CPU, thậm chí mọi giao tiếp dữ liệu với bên ngoài cấu trúc chip như việc nhập/ xuất dữ liệu với memory (RAM) cũng là tác vụ I/O. Trong kiến trúc máy tính, sự kết hợp giữa CPU và bộ nhớ chính (main memory – RAM) được coi là bộ não của máy tính, mọi thao tác truyền dữ liệu với bộ đôi CPU/Memory, ví dụ đọc ghi dữ liệu từ ổ cứng đều được coi là tác vụ I/O. Do các thành phần bên trong kiến trúc phụ thuộc vào dữ liệu từ các thành phần khác, mà tốc độ giữa các thành phần này là khác nhau, khi một thành phần hoạt động không theo kịp thành phần khác, khiến thành phần khác phải rảnh rỗi vì không có dữ liệu làm việc, thành phần chậm chạp kia trở thành một bottle-neck, kéo lùi hiệu năng của toàn bộ hệ thống. Dựa theo các thành phần của kiến trúc máy tính hiện đại, tốc độ thực hiện tiến trình phụ thuộc:
Do tốc độ I/O thường rất chậm so với các thành phần còn lại, bottle-neck thường xuyên xảy ra ở đây. Người ta thường xét đến I/O Bound và CPU Bound, cố gắng đưa các process bị giới hạn bởi I/O bound về CPU bound để tận dụng tối đa hiệu năng. |