I / O không chặn hoặc không đồng bộ trong Node.js là gì?


136

Trong ngữ cảnh của các công cụ Javascript phía máy chủ, I / O không chặn hoặc I / O không đồng bộ là gì? Tôi thấy điều này được đề cập như một lợi thế so với việc triển khai phía máy chủ Java.


3
Thật hữu ích khi nghĩ về các thẻ script trong môi trường trình duyệt để hiểu khái niệm này. Zakas có một bài viết tuyệt vời về điều này - một vài phần đầu tiên đủ để giải thích khái niệm chặn: nczonline.net/blog/2010/08/10/what-is-a-non-blocking-script
netpoetica

Câu trả lời:


317

Đồng bộ vs không đồng bộ

Thực thi đồng bộ thường đề cập đến việc thực thi mã theo trình tự. Thực thi không đồng bộ đề cập đến thực thi không chạy trong chuỗi nó xuất hiện trong mã. Trong ví dụ sau, hoạt động đồng bộ làm cho các cảnh báo bắn theo thứ tự. Trong hoạt động không đồng bộ, trong khi alert(2)dường như thực thi thứ hai, thì không.

Đồng bộ: 1,2,3

alert(1);
alert(2);
alert(3);

Không đồng bộ: 1,3,2

alert(1);
setTimeout(() => alert(2), 0);
alert(3);

Chặn vs Không chặn

Chặn liên quan đến các hoạt động chặn thực hiện thêm cho đến khi hoạt động đó kết thúc. Không chặn đề cập đến mã không chặn thực thi. Trong ví dụ đã cho, localStoragelà một hoạt động chặn vì nó ngăn chặn việc thực thi để đọc. Mặt khác, fetchlà một hoạt động không chặn vì nó không bị đình trệ alert(3)khi thực hiện.

// Blocking: 1,... 2
alert(1);
var value = localStorage.getItem('foo');
alert(2);

// Non-blocking: 1, 3,... 2
alert(1);
fetch('example.com').then(() => alert(2));
alert(3);

Ưu điểm

Một lợi thế của các hoạt động không đồng bộ, không đồng bộ là bạn có thể tối đa hóa việc sử dụng một CPU cũng như bộ nhớ.

Ví dụ đồng bộ, chặn

Một ví dụ về các hoạt động chặn, đồng bộ là cách một số máy chủ web như Java hoặc PHP xử lý các yêu cầu IO hoặc mạng. Nếu mã của bạn đọc từ một tệp hoặc cơ sở dữ liệu, mã của bạn sẽ "chặn" mọi thứ sau khi nó thực thi. Trong khoảng thời gian đó, máy của bạn đang giữ bộ nhớ và thời gian xử lý cho một luồng không làm gì cả .

Để phục vụ các yêu cầu khác trong khi luồng đó bị đình trệ phụ thuộc vào phần mềm của bạn. Những gì hầu hết các phần mềm máy chủ làm là sinh ra nhiều luồng hơn để phục vụ các yêu cầu bổ sung. Điều này đòi hỏi nhiều bộ nhớ tiêu thụ và xử lý nhiều hơn.

Ví dụ không đồng bộ, không chặn

Các máy chủ không đồng bộ, không chặn - như các máy chủ được tạo trong Node - chỉ sử dụng một luồng để phục vụ tất cả các yêu cầu. Điều này có nghĩa là một thể hiện của Node tận dụng tối đa một luồng. Các nhà sáng tạo đã thiết kế nó với tiền đề rằng các hoạt động I / O và mạng là nút cổ chai.

Khi các yêu cầu đến máy chủ, chúng được phục vụ từng cái một. Tuy nhiên, khi mã được phục vụ cần truy vấn DB chẳng hạn, nó sẽ gửi cuộc gọi lại đến hàng đợi thứ hai và luồng chính sẽ tiếp tục chạy (nó không chờ). Bây giờ khi hoạt động DB hoàn thành và trả về, cuộc gọi lại tương ứng đã rút ra khỏi hàng đợi thứ hai và xếp hàng trong hàng thứ ba nơi chúng đang chờ thực thi. Khi động cơ có cơ hội thực hiện một cái gì đó khác (như khi ngăn xếp thực thi bị xóa), nó chọn một cuộc gọi lại từ hàng đợi thứ ba và thực hiện nó.


5
Tôi không chắc là tôi hiểu đoạn thứ 2 của bạn trong Chặn trong PHP . Bạn có nói rằng, "Mặc dù PHP thường chặn trên IO, nhưng không phải vì HĐH tự động xử lý các hoạt động IO."? Hoặc, bạn đang nói rằng đây không phải là vấn đề trong PHP vì PHP tự động tạo một luồng mới cho mỗi yêu cầu để một yêu cầu bị chặn không dừng toàn bộ môi trường PHP? (Tôi đoán là người sau ..)
dcow

6
Đó là cái sau.
Joseph

2
chờ đã, nếu nó có nghĩa là cái thứ hai, thì những lợi thế không chặn I / O PHP (như ReacPHP hay cái gì khác) so với cái chặn. vẫn còn nhầm lẫn
Sunu Pinasthika Fajar

5
@CharlieParker Có. Hoạt động async chạy song song với mã của bạn. Nhưng cuộc gọi lại "quay lại" với kết quả của hoạt động async được xếp hàng để thực thi trong mã chính khi mã chính không bận.
Joseph

2
@CharlieParker Đây là một bài viết liên quan nhiều hơn đến nội bộ của cơ chế async.
Joseph

7
var startTime = new Date().getTime();
var getEndTime = () => {
    var tempEndTime = new Date().getTime();
    var second = (tempEndTime - startTime)/1000
    return `took ${second} sec...to finish\n`
}

console.log('1: start App', getEndTime())
setTimeout(()=>{
    console.log('2: setTimeout', getEndTime())
}, 1000)
console.log('3: End App', getEndTime())

// console -> Process Order:  1 -> 3 -> 2

Mã ví dụ

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.