Chúng ta hãy cùng nhau xem xét các tình huống sau cùng tại một cửa hàng McDonald’s
Cửa hàng đồng bộ(synchronous):
Bạn cùng người ấy đi mua đồ ăn nhanh, bạn đứng xếp hàng trong khi một nhân viên thu ngân nhận đơn đặt hàng từ những người trước mặt bạn. 😍
Nhân viên thu ngân đồng thời là đầu bếp nhận đơn đặt hàng từ những người trước mặt bạn.
Mọi người trước bạn đang đợi bánh mì kẹp thịt của họ sẵn sàng trước khi rời quầy vì người nhân viên thu ngân sẽ đi và chuẩn bị bánh mì kẹp thịt ngay lập tức trước khi nhận đơn đặt hàng tiếp theo.
Rồi cuối cùng cũng đến lượt bạn, bạn đặt hàng 2 chiếc bánh mì kẹp thịt rất lạ mắt cho người ấy và bạn.
Bạn trả tiền 💸.
Nhân viên thu ngân đi vào bếp.
Bạn đợi, đứng trước quầy 🕙, để không ai khác lấy bánh mì kẹp thịt của bạn trước bạn, vì không có số cho lượt.
Đây là công việc “đồng bộ“, bạn “đồng bộ” với thu ngân/đầu bếp 👨🍳. Bạn phải đợi 🕙 (blocking) và có mặt đúng lúc nhân viên thu ngân/đầu bếp 👨🍳 ăn xong bánh mì kẹp thịt và đưa cho bạn, nếu không, người khác có thể lấy mất.
Sau đó, nhân viên thu ngân/đầu bếp 👨🍳 của bạn cuối cùng cũng quay lại với bánh mì kẹp thịt của bạn, sau một thời gian dài chờ đợi 🕙 ở đó trước quầy.
Bạn lấy bánh mì kẹp thịt của mình và đi đến bàn với người bạn thích.
Bạn chỉ cần ăn chúng, và bạn đã hoàn thành. ⏹
Không có nhiều cuộc nói chuyện hay tán tỉnh vì hầu hết thời gian là chờ đợi 🕙 (blocking) trước quầy. 😞
Cửa hàng bất đồng bộ(asynchronous)
Ở một cửa hàng khác, mọi việc diễn ra một cách trôi chảy hơn.
Thu ngân và đầu bếp là hai người khác nhau. Nhân viên thu ngân thông báo với đầu bếp trong bếp để họ biết rằng họ phải chuẩn bị bánh mì kẹp thịt cho bạn (mặc dù họ hiện đang chuẩn bị bánh mì kẹp thịt cho những khách hàng trước đó).
Bạn trả tiền. 💸 .Nhân viên thu ngân cung cấp cho bạn số lần lượt của bạn.
Trong khi chờ đợi, bạn đi cùng người ấy và chọn một bàn, bạn ngồi nói chuyện rất lâu với người ấy (vì bánh mì kẹp thịt của bạn rất cầu kỳ và mất một chút thời gian để chuẩn bị) – nonblocking.
Trong lúc chờ đợi và nói chuyện với crush, thỉnh thoảng bạn kiểm tra số hiển thị trên quầy xem đã đến lượt mình chưa.
Rồi đến một lúc nào đó, cuối cùng cũng đến lượt bạn. Bạn đi đến quầy, lấy bánh mì kẹp thịt của bạn và trở lại bàn.
Lần này, cuộc trò chuyện có thể diễn ra (nonblocking) trong lúc nhà hoàng chuẩn bị bánh. Bạn và người bạn thích ăn bánh mì kẹp thịt và có một khoảng thời gian vui vẻ. ✨
Cửa hàng song song(parallel)
Bạn cùng người ấy đi mua đồ ăn nhanh song song.
Lần này có nhiều (5) quầy tiếp nhận phục vụ hoạt động cùng lúc. Phía sau mỗi quầy là một bếp riêng biệt hoạt động.
Như vậy, cửa hàng này có thể tiếp nhận gấp 5 lần các cửa hàng trước. Nó cũng có thể hoạt động một cách đồng bộ hoặc bất đồng bộ như 2 cửa hàng phía trên.
Trong lập trình:
Lập trình đồng bộ (synchronous) là cách lập trình mà các hoạt động của chương trình sẽ được thực hiện tuần tự. Với ưu điểm là các hoạt động xảy ra tuần tự nên có thể dễ quản lý, dễ debug và phát hiện vấn đề khi xảy ra lỗi.
Đa số các công việc cần xử lý là xử lý đồng bộ.
Ngược lại, lập trình bất đồng bộ(asynchronous) là cách lập trình cho phép các hoạt động thực hiện không theo tuần tự. Có nghĩa là ngôn ngữ 💬 có cách để báo cho máy tính/chương trình 🤖 rằng tại một thời điểm nào đó, máy tính 🤖 sẽ phải đợi một thứ khác hoàn thành ở một nơi khác. Vì vậy, trong thời gian đó, máy tính có thể đi làm một số công việc khác.
“Chờ một cái gì đó khác” thường đề cập đến các hoạt động I/O tương đối “chậm” (so với tốc độ của bộ xử lý và bộ nhớ RAM), như chờ đợi:
- nội dung được đọc/ghi vào đĩa
- gọi API
- một truy vấn cơ sở dữ liệu
- một logic phức tạp
Rõ ràng, khi gặp các tác vụ phải chờ đợi như vậy, xử lý bất đồng bộ luôn tối ưu hơn về mặt quản lý resource đồng thời cũng đem lại trải nghiệm tốt hơn cho người dùng.
Điểm cần lưu ý là xử lý bất đồng bộ luôn đi kèm theo sự phức tạp trong quá trình quản lý các tác vụ và codebase.
Luồng (thread) và tiến trình (process)
Không quan trọng chúng ta sử dụng ngôn ngữ nào: C, Lisp hay PHP, khi mã của được biên dịch hoặc thông dịch thành một mã nhị phân. Khi thực thi mã nhị phân này, chương trình cần một số tài nguyên từ HĐH để chạy: không gian địa chỉ bộ nhớ, PID (ID tiến trình) và một số tài nguyên khác. Có thể có nhiều phiên bản của cùng một chương trình đang chạy, mỗi phiên bản là một tiến trình riêng biệt trong HĐH. Việc chuyển đổi từ một tiến trình này sang một tiến trình khác đòi hỏi một khoảng thời gian để lưu và tải các thanh ghi CPU, bộ nhớ và các tài nguyên khác.
Vì vậy, khi tiến trình bắt đầu và nó nhận được bộ nhớ và tài nguyên của riêng mình. Tất cả các luồng trong tiến trình chia sẻ bộ nhớ và tài nguyên đó. Mỗi tiến trình có ít nhất một luồng được gọi là luồng chính. Khi luồng chính được thực hiện xong, tiến trình và chương trình sẽ tự thoát.
Hệ điều hành chạy một luồng nhất định trên lõi bộ xử lý (processor core). Tuy nhiên, lõi bộ xử lý chỉ có thể thực thi một luồng duy nhất cùng một lúc. Nó không có khái niệm chạy nhiều luồng đồng thời. Hệ điều hành có thể tạo ảo giác chạy nhiều luồng (multithreading) cùng một lúc bằng cách chạy từng luồng trong một khoảng thời gian nhỏ và liên tục chuyển đổi giữa các luồng. Bằng cách này, ta có thể thực thi hai chương trình single-thread (hoặc một chương trình đa luồng – multiple thread) trên CPU lõi đơn.
Nếu sử dụng nhiều lõi bộ xử lý, thì các luồng CÓ THỂ thực thi cùng một lúc (parallel). Hệ điều hành có thể phân bổ thời gian cho một luồng trên lõi bộ xử lý đầu tiên, sau đó phân bổ cùng một khối thời gian cho một luồng khác trên lõi bộ xử lý khác.
Xử lý song song yêu cầu phần cứng với nhiều đơn vị xử lý. Với CPU một lõi, bạn có thể đạt được bất đồng bộ nhưng KHÔNG THỂ có song song.
Từ các ví dụ trên, chúng ta cũng thấy rằng bất đồng bộ và song song đều liên quan đến “những điều khác nhau xảy ra ít nhiều cùng một lúc”. Nhưng các chi tiết giữa bất đồng bộ và song song mang tính chất hoàn toàn khác nhau.
Link bài viết gốc: https://fastapi.tiangolo.com/id/async/?h=concurrency
2 Replies to “Đồng bộ bất đồng bộ và song song”