Sự khác biệt giữa các cuộc gọi không đồng bộ và không chặn là gì? Ngoài ra giữa chặn và cuộc gọi đồng bộ (với ví dụ xin vui lòng)?
Sự khác biệt giữa các cuộc gọi không đồng bộ và không chặn là gì? Ngoài ra giữa chặn và cuộc gọi đồng bộ (với ví dụ xin vui lòng)?
Câu trả lời:
Trong nhiều trường hợp, chúng là những cái tên khác nhau cho cùng một thứ, nhưng trong một số bối cảnh chúng lại khá khác nhau. Vì vậy, nó phụ thuộc. Thuật ngữ không được áp dụng theo cách hoàn toàn nhất quán trong toàn bộ ngành công nghiệp phần mềm.
Ví dụ: trong API ổ cắm cổ điển, ổ cắm không chặn là ổ cắm đơn giản trả về ngay lập tức với thông báo lỗi "sẽ chặn" đặc biệt, trong khi ổ cắm chặn sẽ bị chặn. Bạn phải sử dụng một chức năng riêng biệt như select
hoặc poll
để tìm hiểu khi nào là thời điểm tốt để thử lại.
Nhưng các ổ cắm không đồng bộ (như được hỗ trợ bởi các ổ cắm Windows) hoặc mẫu IO không đồng bộ được sử dụng trong .NET, sẽ thuận tiện hơn. Bạn gọi một phương thức để bắt đầu một thao tác và khung sẽ gọi lại cho bạn khi hoàn thành. Ngay cả ở đây, có những khác biệt cơ bản. Ổ cắm Win32 không đồng bộ "sắp xếp" kết quả của chúng vào một luồng GUI cụ thể bằng cách chuyển các thông báo Window, trong khi IO không đồng bộ .NET được phân luồng tự do (bạn không biết cuộc gọi lại của bạn sẽ được gọi là gì).
Vì vậy, họ không luôn luôn có nghĩa là điều tương tự. Để chưng cất ví dụ về socket, chúng ta có thể nói:
đồng bộ / không đồng bộ là để mô tả mối quan hệ giữa hai mô-đun.
chặn / không chặn là để mô tả tình huống của một mô-đun.
Một ví dụ:
Mô-đun X: "Tôi".
Mô-đun Y: "hiệu sách".
X hỏi Y: bạn có một cuốn sách tên là "c ++ primer" không?
1) chặn: trước khi Y trả lời X, X tiếp tục chờ câu trả lời. Bây giờ X (một mô-đun) đang chặn. X và Y là hai luồng hoặc hai tiến trình hay một luồng hoặc một tiến trình? chúng tôi không biết.
2) không chặn: trước khi Y trả lời X, X chỉ rời khỏi đó và làm những việc khác. X có thể quay lại sau mỗi hai phút để kiểm tra xem Y đã hoàn thành công việc chưa? Hay X sẽ không quay lại cho đến khi Y gọi anh ta? Chúng tôi không biết. Chúng ta chỉ biết rằng X có thể làm những việc khác trước khi Y hoàn thành công việc của mình. Ở đây X (một mô-đun) là không chặn. X và Y là hai luồng hoặc hai tiến trình hay một tiến trình? chúng tôi không biết. NHƯNG chúng tôi chắc chắn rằng X và Y không thể là một chủ đề.
3) đồng bộ: trước khi Y trả lời X, X tiếp tục chờ câu trả lời. Điều đó có nghĩa là X không thể tiếp tục cho đến khi Y hoàn thành công việc. Bây giờ chúng ta nói: X và Y (hai mô-đun) là đồng bộ. X và Y là hai luồng hoặc hai tiến trình hay một luồng hoặc một tiến trình? chúng tôi không biết.
4) không đồng bộ: trước khi Y trả lời X, X rời khỏi đó và X có thể thực hiện các công việc khác. X sẽ không quay lại cho đến khi Y gọi anh ta. Bây giờ chúng ta nói: X và Y (hai mô-đun) không đồng bộ. X và Y là hai luồng hoặc hai tiến trình hay một tiến trình? chúng tôi không biết. NHƯNG chúng tôi chắc chắn rằng X và Y không thể là một chủ đề.
Hãy chú ý đến hai câu in đậm ở trên. Tại sao câu in đậm trong 2) chứa hai trường hợp trong khi câu in đậm trong 4) chỉ chứa một trường hợp? Đây là chìa khóa của sự khác biệt giữa không chặn và không đồng bộ.
Dưới đây là một ví dụ điển hình về việc không chặn và đồng bộ:
// thread X
while (true)
{
msg = recv(Y, NON_BLOCKING_FLAG);
if (msg is not empty)
{
break;
}
sleep(2000); // 2 sec
}
// thread Y
// prepare the book for X
send(X, book);
Bạn có thể thấy rằng thiết kế này không chặn (bạn có thể nói rằng hầu hết thời gian vòng lặp này làm điều gì đó vô nghĩa nhưng trong mắt CPU, X đang chạy, điều đó có nghĩa là X không chặn) trong khi X và Y đồng bộ vì X có thể Sẽ không tiếp tục làm bất kỳ điều gì khác (X không thể nhảy ra khỏi vòng lặp) cho đến khi nhận được cuốn sách từ Y.
Thông thường trong trường hợp này, việc chặn X sẽ tốt hơn nhiều vì việc không chặn dành nhiều tài nguyên cho một vòng lặp ngu ngốc. Nhưng ví dụ này là tốt để giúp bạn hiểu thực tế: không chặn không có nghĩa là không đồng bộ.
Bốn từ làm chúng ta dễ nhầm lẫn, điều chúng ta nên nhớ là bốn từ phục vụ cho việc thiết kế kiến trúc. Tìm hiểu về cách thiết kế một kiến trúc tốt là cách duy nhất để phân biệt chúng.
Ví dụ: chúng tôi có thể thiết kế một loại kiến trúc như vậy:
// Module X = Module X1 + Module X2
// Module X1
while (true)
{
msg = recv(many_other_modules, NON_BLOCKING_FLAG);
if (msg is not null)
{
if (msg == "done")
{
break;
}
// create a thread to process msg
}
sleep(2000); // 2 sec
}
// Module X2
broadcast("I got the book from Y");
// Module Y
// prepare the book for X
send(X, book);
Trong ví dụ ở đây, chúng ta có thể nói rằng
Nếu bạn cần, bạn cũng có thể mô tả các chủ đề được tạo trong X1 bằng bốn từ.
Điều quan trọng hơn là: khi nào chúng ta sử dụng đồng bộ thay vì không đồng bộ? Khi nào chúng ta sử dụng chặn thay vì không chặn?
Tại sao Nginx không chặn? Tại sao Apache bị chặn?
Để đưa ra lựa chọn tốt, bạn phải phân tích nhu cầu của mình và kiểm tra hiệu suất của các kiến trúc khác nhau. Không có một kiến trúc như vậy phù hợp cho nhiều nhu cầu khác nhau.
Đặt câu hỏi này trong ngữ cảnh của NIO và NIO.2 trong java 7, async IO tiến bộ hơn một bước so với không chặn. Với các cuộc gọi không chặn java NIO, người ta sẽ đặt tất cả các kênh (SocketChannel, ServerSocketChannel, FileChannel, v.v.) bằng cách gọi AbstractSelectableChannel.configureBlocking(false)
. Sau những IO gọi trở lại, tuy nhiên, bạn có thể sẽ vẫn cần phải kiểm soát kiểm tra chẳng hạn như nếu và khi nào thì đọc / ghi lần nữa, vv
Ví dụ,
while (!isDataEnough()) {
socketchannel.read(inputBuffer);
// do something else and then read again
}
Với api không đồng bộ trong java 7, các điều khiển này có thể được thực hiện theo những cách linh hoạt hơn. Một trong 2 cách là sử dụng CompletionHandler
. Lưu ý rằng cả hai read
cuộc gọi đều không chặn.
asyncsocket.read(inputBuffer, 60, TimeUnit.SECONDS /* 60 secs for timeout */,
new CompletionHandler<Integer, Object>() {
public void completed(Integer result, Object attachment) {...}
public void failed(Throwable e, Object attachment) {...}
}
}
FileChannel
không thể chọn và không thể được cấu hình để không chặn.
Như bạn có thể thấy từ vô số câu trả lời khác nhau (và thường loại trừ lẫn nhau), nó phụ thuộc vào người bạn hỏi. Trong một số đấu trường, các điều khoản là đồng nghĩa. Hoặc mỗi người có thể đề cập đến hai khái niệm tương tự nhau:
Trong cả hai trường hợp, ý định là cho phép chương trình không bị chặn chờ quá trình chậm hoàn thành - cách chương trình dự kiến sẽ đáp ứng là sự khác biệt thực sự duy nhất. Thuật ngữ nào đề cập đến cũng thay đổi từ lập trình viên sang lập trình viên, ngôn ngữ sang ngôn ngữ hoặc nền tảng sang nền tảng. Hoặc các thuật ngữ có thể đề cập đến các khái niệm hoàn toàn khác nhau (chẳng hạn như việc sử dụng đồng bộ / không đồng bộ liên quan đến lập trình luồng).
Xin lỗi, nhưng tôi không tin có một câu trả lời đúng duy nhất là đúng trên toàn cầu.
Một cuộc gọi không chặn trả về ngay lập tức với bất kỳ dữ liệu nào có sẵn: toàn bộ số byte được yêu cầu, ít hơn hoặc không có gì cả.
Một cuộc gọi không đồng bộ yêu cầu chuyển sẽ được thực hiện trong toàn bộ (toàn bộ) nhưng sẽ hoàn thành vào một thời điểm nào đó trong tương lai.
Không chặn: Chức năng này sẽ không chờ trong khi trên ngăn xếp.
Không đồng bộ: Công việc có thể tiếp tục thay mặt cho chức năng gọi sau khi cuộc gọi đó rời khỏi ngăn xếp
Đồng bộ được định nghĩa là xảy ra cùng một lúc.
Không đồng bộ được định nghĩa là không xảy ra cùng một lúc.
Đây là những gì gây ra sự nhầm lẫn đầu tiên. Đồng bộ thực sự là những gì được gọi là song song. Trong khi không đồng bộ là tuần tự, làm điều này, sau đó làm điều đó.
Bây giờ toàn bộ vấn đề là về việc mô hình hóa một hành vi không đồng bộ, bởi vì bạn đã có một số hoạt động cần phản hồi của người khác trước khi nó có thể bắt đầu. Vì vậy, đây là một vấn đề phối hợp, làm sao bạn biết rằng bây giờ bạn có thể bắt đầu hoạt động đó?
Giải pháp đơn giản nhất được gọi là chặn.
Chặn là khi bạn chỉ cần chọn chờ đợi việc khác được thực hiện và trả lại cho bạn phản hồi trước khi chuyển sang thao tác cần thiết.
Vì vậy, nếu bạn cần cho bơ vào bánh mì nướng, và do đó trước tiên bạn cần nướng bánh mì. Cách bạn phối hợp với chúng là lần đầu tiên bạn nướng bánh mì, sau đó nhìn chằm chằm vô tận vào lò nướng bánh cho đến khi nó bật bánh mì nướng, và sau đó bạn sẽ tiến hành bôi bơ lên chúng.
Đó là giải pháp đơn giản nhất và hoạt động rất tốt. Không có lý do thực sự để không sử dụng nó, trừ khi bạn cũng có những thứ khác bạn cần phải làm mà không cần sự phối hợp với các hoạt động. Ví dụ, làm một số món ăn. Tại sao phải chờ đợi nhìn chằm chằm vào máy nướng bánh mì liên tục để bánh mì nướng bật lên, khi bạn biết rằng nó sẽ mất một chút thời gian và bạn có thể rửa toàn bộ món ăn trong khi nó kết thúc?
Đó là nơi hai giải pháp khác được gọi là không chặn và không đồng bộ đi vào hoạt động.
Không chặn là khi bạn chọn thực hiện những việc không liên quan khác trong khi bạn chờ thao tác được thực hiện. Kiểm tra lại tính khả dụng của phản hồi khi bạn thấy phù hợp.
Vì vậy, thay vì nhìn vào máy nướng bánh cho nó bật. Bạn đi và rửa toàn bộ một món ăn. Và sau đó bạn nhìn trộm lò nướng bánh để xem bánh mì nướng đã bật chưa. Nếu họ không có, bạn đi rửa một món ăn khác, kiểm tra lại máy nướng bánh giữa mỗi món ăn. Khi bạn thấy bánh mì nướng đã bật lên, bạn dừng rửa chén, và thay vào đó bạn lấy bánh mì nướng và chuyển sang đặt bơ lên chúng.
Phải liên tục kiểm tra các bánh mì nướng có thể gây phiền nhiễu, hãy tưởng tượng máy nướng bánh mì đang ở trong một phòng khác. Ở giữa các món ăn, bạn lãng phí thời gian của mình đến phòng khác để kiểm tra bánh mì nướng.
Ở đây không đồng bộ.
Không đồng bộ là khi bạn chọn thực hiện những việc không liên quan khác trong khi bạn chờ thao tác được thực hiện. Thay vì kiểm tra trên đó, bạn ủy thác công việc kiểm tra cho một thứ khác, có thể là chính hoạt động hoặc người theo dõi, và bạn có thông báo đó và có thể can thiệp vào bạn khi phản hồi có sẵn để bạn có thể tiến hành hoạt động khác cần nó
Đó là một thuật ngữ kỳ lạ. Không có nhiều ý nghĩa, vì tất cả các giải pháp này là cách để tạo ra sự phối hợp không đồng bộ của các nhiệm vụ phụ thuộc. Đó là lý do tại sao tôi thích gọi nó là sự kiện.
Vì vậy, đối với điều này, bạn quyết định nâng cấp máy nướng bánh mì của bạn để nó phát ra tiếng bíp khi các bánh mì nướng được hoàn thành. Bạn tình cờ liên tục lắng nghe, ngay cả khi bạn đang làm món ăn. Khi nghe tiếng bíp, bạn xếp hàng trong trí nhớ rằng ngay sau khi rửa xong món ăn hiện tại, bạn sẽ dừng lại và đặt bơ lên bánh mì nướng. Hoặc bạn có thể chọn cách xen vào việc rửa chén đĩa hiện tại và xử lý bánh mì nướng ngay lập tức.
Nếu bạn gặp khó khăn khi nghe tiếng bíp, bạn có thể nhờ đối tác của mình xem máy nướng bánh mì cho bạn và nói với bạn khi nào bánh mì nướng đã sẵn sàng. Đối tác của bạn có thể tự mình chọn bất kỳ chiến lược nào trong ba chiến lược trên để phối hợp nhiệm vụ xem máy nướng bánh mì và cho bạn biết khi nào họ sẵn sàng.
Ở một lưu ý cuối cùng, thật tốt khi hiểu rằng trong khi không chặn và không đồng bộ (hoặc những gì tôi muốn gọi là sự kiện) sẽ cho phép bạn làm những việc khác trong khi chờ đợi, bạn cũng không có. Bạn có thể chọn liên tục lặp lại việc kiểm tra trạng thái của một cuộc gọi không chặn, không làm gì khác. Điều đó thường tệ hơn việc chặn mặc dù (như tìm máy nướng bánh mì, sau đó quay lại, sau đó quay lại cho đến khi hoàn thành), vì vậy rất nhiều API không chặn cho phép bạn chuyển sang chế độ chặn từ nó. Đối với sự kiện, bạn chỉ có thể chờ đợi cho đến khi bạn được thông báo. Nhược điểm trong trường hợp đó là việc thêm thông báo rất phức tạp và có khả năng tốn kém khi bắt đầu. Bạn phải mua một máy nướng bánh mì mới có chức năng tiếng bíp, hoặc thuyết phục đối tác của bạn xem nó cho bạn.
Và một điều nữa, bạn cần nhận ra sự đánh đổi cả ba cung cấp. Một cái rõ ràng không tốt hơn những cái khác. Hãy nghĩ về ví dụ của tôi. Nếu máy nướng bánh mì của bạn quá nhanh, bạn sẽ không có thời gian để rửa chén đĩa, thậm chí không bắt đầu rửa nó, đó là máy nướng bánh mì của bạn nhanh như thế nào. Bắt đầu với một cái gì đó khác trong trường hợp đó chỉ là một sự lãng phí thời gian và nỗ lực. Chặn sẽ làm. Tương tự, nếu rửa một món ăn sẽ mất nhiều thời gian hơn 10 lần thì nướng. Bạn phải tự hỏi những gì quan trọng hơn để hoàn thành? Bánh mì nướng có thể bị lạnh và cứng vào thời điểm đó, không đáng, việc chặn cũng sẽ làm. Hoặc bạn nên chọn những thứ nhanh hơn để làm trong khi bạn chờ đợi. Rõ ràng hơn, nhưng câu trả lời của tôi đã khá dài, quan điểm của tôi là bạn cần suy nghĩ về tất cả những điều đó, và sự phức tạp của việc thực hiện từng quyết định xem nó có xứng đáng không, và liệu nó có '
Biên tập:
Mặc dù điều này đã lâu, tôi cũng muốn nó được hoàn thành, vì vậy tôi sẽ thêm hai điểm nữa.
1) Cũng thường tồn tại một mô hình thứ tư được gọi là ghép kênh . Đây là khi bạn chờ đợi một nhiệm vụ, bạn bắt đầu một nhiệm vụ khác và trong khi bạn chờ đợi cả hai, bạn bắt đầu một nhiệm vụ khác, và cứ thế, cho đến khi bạn có nhiều nhiệm vụ bắt đầu và sau đó, bạn chờ đợi, nhưng trên tất cả họ Vì vậy, ngay sau khi bất kỳ được thực hiện, bạn có thể tiến hành xử lý phản hồi của nó, và sau đó quay lại để chờ đợi những người khác. Nó được gọi là ghép kênh, bởi vì trong khi bạn chờ đợi, bạn cần kiểm tra từng nhiệm vụ lần lượt để xem liệu chúng đã hoàn thành chưa, ad vitam, cho đến khi hoàn thành. Đó là một phần mở rộng trên đầu trang không chặn thông thường.
Trong ví dụ của chúng tôi, nó sẽ giống như khởi động máy nướng bánh mì, sau đó là máy rửa chén, rồi lò vi sóng, v.v. Và sau đó chờ đợi bất kỳ ai trong số họ. Nơi bạn sẽ kiểm tra máy nướng bánh mì để xem nó đã xong chưa, nếu không, bạn sẽ kiểm tra máy rửa chén, nếu không, lò vi sóng và xung quanh một lần nữa.
2) Mặc dù tôi tin rằng đó là một sai lầm lớn, đồng bộ thường được sử dụng để chỉ một điều tại một thời điểm. Và không đồng bộ nhiều thứ cùng một lúc. Do đó, bạn sẽ thấy chặn và không chặn đồng bộ được sử dụng để chỉ chặn và không chặn. Và chặn không đồng bộ và không chặn được sử dụng để chỉ đa kênh và sự kiện.
Tôi thực sự không hiểu làm thế nào chúng ta đến đó. Nhưng khi nói đến IO và Tính toán, đồng bộ và không đồng bộ thường đề cập đến những gì được gọi là không chồng chéo và chồng chéo. Đó là, không đồng bộ có nghĩa là IO và Tính toán bị chồng chéo, hay còn gọi là xảy ra đồng thời. Trong khi đồng bộ có nghĩa là chúng không, do đó xảy ra tuần tự. Để không chặn đồng bộ, điều đó có nghĩa là bạn không bắt đầu IO hoặc tính toán khác, bạn chỉ cần chờ đợi và mô phỏng cuộc gọi chặn. Tôi ước mọi người ngừng lạm dụng đồng bộ hóa và không đồng bộ như thế. Vì vậy, tôi không khuyến khích nó.
Chặn cuộc gọi: Điều khiển chỉ trả về khi cuộc gọi hoàn thành.
Không chặn cuộc gọi: Kiểm soát trả về ngay lập tức. Hệ điều hành sau này bằng cách nào đó thông báo cho quá trình cuộc gọi hoàn tất.
Chương trình đồng bộ : Một chương trình sử dụng Chặn cuộc gọi. Để không bị đóng băng trong suốt cuộc gọi, nó phải có 2 luồng trở lên (đó là lý do tại sao nó được gọi là Đồng bộ - các luồng đang chạy đồng bộ).
Chương trình không đồng bộ : Một chương trình sử dụng các cuộc gọi không chặn . Nó có thể chỉ có 1 chủ đề và vẫn còn tương tác.
Các mô hình chặn yêu cầu ứng dụng khởi tạo chặn khi I / O đã bắt đầu. Điều này có nghĩa là không thể chồng chéo xử lý và I / O cùng một lúc. Mô hình không chặn đồng bộ cho phép chồng chéo xử lý và I / O, nhưng nó yêu cầu ứng dụng kiểm tra trạng thái của I / O trên cơ sở định kỳ. Điều này để lại I / O không chặn không đồng bộ, cho phép chồng chéo xử lý và I / O, bao gồm thông báo hoàn thành I / O.
Chặn: điều khiển quay trở lại để gọi các điều kiện tiên quyết sau khi hoàn thành quá trình nguyên thủy (đồng bộ hóa hoặc không đồng bộ)
Không chặn: điều khiển quay trở lại xử lý ngay sau khi gọi