Chủ đề công nhân trong nút. js rất hữu ích để thực hiện các tác vụ JavaScript nặng. Với sự trợ giúp của các luồng, Worker giúp dễ dàng chạy song song các mã javascript, giúp nó nhanh hơn và hiệu quả hơn. Chúng ta có thể thực hiện các tác vụ nặng mà không làm ảnh hưởng đến luồng chính. Chuỗi công nhân không được giới thiệu trong các phiên bản cũ hơn của Nút. Do đó, trước tiên hãy cập nhật Nút của bạn. js để bắt đầu
Bây giờ hãy tạo hai tệp để triển khai luồng như hình bên dưới
tên tệp. công nhân. js
const { workerData, parentPort }
= require[
'worker_threads'
]
console.log[
'Technical Articles on '
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }0
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }1
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }3
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }4_______0_______5_______0_______6
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }7
Ở đây, workerData và parentPort là một phần của Worker Thread. workerData được sử dụng để lấy dữ liệu từ luồng và parentPort được sử dụng để thao tác luồng. Phương thức postMessage[] được sử dụng để đăng thông báo đã cho trong bảng điều khiển bằng cách lấy tên tệp do workerData tìm nạp
tên tệp. mục lục. js
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }8_______45_______
]
const { workerData, parentPort }
2 const { workerData, parentPort }
3
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }4_______42_______5
const { workerData, parentPort }
6 const { workerData, parentPort }
7
const { workerData, parentPort }
9_______42_______6
1
2_______43_______3
4
6_______43_______7
8
6_______44_______1= require[
2
6_______44_______5= require[
6
= require[
7= require[
8 = require[
9
2_______45_______1const { workerData, parentPort }
6 'worker_threads'
3
'worker_threads'
4'worker_threads'
5 'worker_threads'
6
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }7
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }4_______0_______7
]
1
]
3_______42_______2 ]
5
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }4_______46_______7
]
8]
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }4
1]
1
4
5
6
Ở đây, hàm runService[] trả về một Promise và chạy worker thread. Hàm run[] được sử dụng để gọi hàm runService[] và đưa ra giá trị cho workerData
Bước để chạy ứng dụng này. Chạy lệnh sau
node index.js
Đầu ra cho lệnh sau ở trên được hiển thị bên dưới
Technical Articles on GeeksForGeeks { fileName: 'GeeksForGeeks', status: 'Done' }
Phần kết luận. Với worker thread, chúng ta có thể đạt được một ứng dụng hiệu quả mà không tạo ra tình huống bế tắc. Đây là cách bạn có thể nhập luồng công nhân trong một ứng dụng và triển khai luồng
một quá trình. một quy trình là một đối tượng toàn cầu có thể được truy cập ở mọi nơi và có thông tin về những gì đang được thực thi tại một thời điểm
một chủ đề. là đơn luồng có nghĩa là chỉ có một bộ hướng dẫn được thực thi tại một thời điểm trong một quy trình nhất định
Một vòng lặp sự kiện. đây là một trong những khía cạnh quan trọng nhất để hiểu về Node. Đó là thứ cho phép Node không đồng bộ và có I/O không chặn — mặc dù thực tế là JavaScript là một luồng — bằng cách giảm tải các hoạt động cho nhân hệ thống bất cứ khi nào có thể thông qua các cuộc gọi lại, lời hứa và không đồng bộ/chờ đợi
Một phiên bản công cụ JS. đây là một chương trình máy tính thực thi mã JavaScript
một nút. trường hợp js. chương trình máy tính thực thi Node. mã js
Nói cách khác, Node chạy trên một luồng duy nhất và chỉ có một quá trình xảy ra tại một thời điểm trong vòng lặp sự kiện. Một mã, một lần thực thi, [mã không được thực thi song song]. Điều này rất hữu ích vì nó đơn giản hóa cách bạn sử dụng JavaScript mà không phải lo lắng về vấn đề tương tranh
Lý do nó được xây dựng theo cách tiếp cận đó là vì JavaScript ban đầu được tạo cho các tương tác phía máy khách [như tương tác trang web hoặc xác thực biểu mẫu] -- không có gì đòi hỏi sự phức tạp của đa luồng
Nhưng, như với tất cả mọi thứ, có một nhược điểm. nếu bạn có mã sử dụng nhiều CPU, chẳng hạn như các phép tính phức tạp trong tập dữ liệu lớn diễn ra trong bộ nhớ, mã đó có thể chặn thực thi các quy trình khác. Tương tự, nếu bạn đang yêu cầu một máy chủ có mã sử dụng nhiều CPU, mã đó có thể chặn vòng lặp sự kiện và ngăn các yêu cầu khác được xử lý
Một chức năng được coi là "chặn" nếu vòng lặp sự kiện chính phải đợi cho đến khi nó thực hiện xong lệnh tiếp theo. Chức năng “Không chặn” sẽ cho phép vòng lặp sự kiện chính tiếp tục ngay khi nó bắt đầu và thường thông báo cho vòng lặp chính sau khi kết thúc bằng cách gọi “gọi lại”
quy tắc vàng. không chặn vòng lặp sự kiện, cố gắng giữ cho nó chạy và chú ý và tránh mọi thứ có thể chặn chuỗi như cuộc gọi mạng đồng bộ hoặc vòng lặp vô hạn
Điều quan trọng là phải phân biệt giữa hoạt động của CPU và hoạt động I/O [đầu vào/đầu ra]. Như đã đề cập trước đó, mã của Node. js KHÔNG được thực thi song song. Chỉ các hoạt động I/O được chạy song song, bởi vì chúng được thực hiện không đồng bộ
Vì vậy Worker Threads sẽ không giúp ích nhiều cho công việc sử dụng nhiều I/O vì hoạt động I/O không đồng bộ hiệu quả hơn so với Worker có thể. Mục tiêu chính của Công nhân là cải thiện hiệu suất trên các hoạt động sử dụng nhiều CPU chứ không phải hoạt động I/O
Một số giải pháp
Hơn nữa, đã có các giải pháp cho các hoạt động chuyên sâu của CPU. nhiều quy trình [như API cụm] đảm bảo rằng CPU được sử dụng một cách tối ưu
Cách tiếp cận này thuận lợi vì nó cho phép cô lập các quy trình, vì vậy nếu có sự cố xảy ra trong một quy trình, nó sẽ không ảnh hưởng đến các quy trình khác. Họ cũng có sự ổn định và các API giống hệt nhau. Tuy nhiên, điều này đồng nghĩa với việc hy sinh bộ nhớ dùng chung và việc truyền dữ liệu phải thông qua JSON
JavaScript và nút. js sẽ không bao giờ có chủ đề, đây là lý do tại sao
Vì vậy, mọi người có thể nghĩ rằng việc thêm một mô-đun mới vào Node. js sẽ cho phép chúng tôi tạo và đồng bộ hóa các luồng, do đó giải quyết vấn đề về hoạt động sử dụng nhiều CPU
Vâng, không, không thực sự. Nếu các chủ đề được thêm vào, bản chất của ngôn ngữ sẽ thay đổi. Không thể thêm các chủ đề dưới dạng một tập hợp mới các lớp hoặc chức năng có sẵn. Trong các ngôn ngữ hỗ trợ đa luồng [như Java], các từ khóa như “được đồng bộ hóa” giúp cho phép nhiều luồng đồng bộ hóa
Ngoài ra, một số kiểu số không phải là nguyên tử, nghĩa là nếu bạn không đồng bộ hóa chúng, cuối cùng bạn có thể có hai luồng thay đổi giá trị của một biến và kết quả là sau khi cả hai luồng đã truy cập vào biến đó, biến đó có một vài byte bị thay đổi bởi . Ví dụ, trong hoạt động đơn giản của 0. 1 + 0. 2 có 17 số thập phân trong JavaScript [số thập phân tối đa]
var x = 0.1 + 0.2; // x will be 0.30000000000000004
Nhưng số học dấu phẩy động không phải lúc nào cũng chính xác 100%. Vì vậy, nếu không được đồng bộ hóa, một số thập phân có thể bị thay đổi khi sử dụng Công nhân, dẫn đến các số không giống nhau
Giải pháp tốt nhất
Giải pháp tốt nhất cho hiệu suất CPU là Worker Threads. Các trình duyệt đã có khái niệm về Công nhân từ lâu
thay vì có
- một quy trình
- một chủ đề
- Một vòng lặp sự kiện
- Một phiên bản công cụ JS
- một nút. trường hợp js
Chủ đề công nhân có
- một quy trình
- Nhiều chủ đề
- Một vòng lặp sự kiện trên mỗi luồng
- Một phiên bản Công cụ JS cho mỗi luồng
- một nút. js Instance cho mỗi chủ đề
Như chúng ta có thể thấy trong hình ảnh sau đây
Mô-đun worker_threads
cho phép sử dụng các luồng thực thi JavaScript song song. Để truy cập nó
const worker = require['worker_threads'];
Chủ đề công nhân đã có sẵn kể từ Node. js 10, nhưng vẫn đang trong giai đoạn thử nghiệm
Bắt đầu với giám sát hiệu suất tác động thấp Tạo tài khoản NodeSource của bạn
Điều lý tưởng là có nhiều nút. js trong cùng một quy trình. Với Worker thread, một thread có thể kết thúc tại một thời điểm nào đó và nó không nhất thiết phải kết thúc process cha. Đó không phải là một cách làm tốt đối với các tài nguyên được Công nhân phân bổ để tồn tại khi Công nhân không còn nữa-- đó là rò rỉ bộ nhớ và chúng tôi không muốn điều đó. Chúng tôi muốn nhúng Node. js vào chính nó, đưa Node. js khả năng tạo một chủ đề mới và sau đó tạo một Nút mới. js bên trong luồng đó;
Điều gì khiến Worker Threads trở nên đặc biệt
ArrayBuffers
để chuyển bộ nhớ từ luồng này sang luồng khácSharedArrayBuffer
sẽ có thể truy cập được từ một trong hai luồng. Nó cho phép bạn chia sẻ bộ nhớ giữa các luồng [giới hạn ở dữ liệu nhị phân]Atomics
có sẵn, nó cho phép bạn thực hiện một số quy trình đồng thời, hiệu quả hơn và cho phép bạn triển khai các biến điều kiện trong JavaScriptMessagePort
, được sử dụng để liên lạc giữa các chủ đề khác nhau. Nó có thể được sử dụng để truyền dữ liệu có cấu trúc, vùng bộ nhớ và các MessagePort khác giữa các Worker khác nhau
0 đại diện cho một kênh liên lạc hai chiều không đồng bộ được sử dụng để liên lạc giữa các luồng khác nhauconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
1 được sử dụng để truyền dữ liệu khởi động. Một giá trị JavaScript tùy ý chứa một bản sao của dữ liệu được truyền tới hàm tạo Worker của luồng này. Dữ liệu được sao chép như thể sử dụngconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
2const { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
API
3 => Lớpconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
4 đại diện cho một luồng thực thi JavaScript độc lập vàconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
5 là một phiên bản của cổng thông báoconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
6 hoặcconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
7 => là hai cách chính để bắt đầu worker [chuyển tên tệp hoặc mã mà bạn muốn thực thi]. Nên sử dụng tên tệp trong sản xuấtconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
8,const { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
9 => để nghe tin nhắn và gửi chúng giữa các chủ đề khác nhauconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
0,$ node --experimental-worker test.js { pong: ‘ping’ }
1 => Thư được gửi bằng cách sử dụng$ node --experimental-worker test.js { pong: ‘ping’ }
2 sẽ có sẵn trong chuỗi gốc bằng cách sử dụng$ node --experimental-worker test.js { pong: ‘ping’ }
3 và thư được gửi từ chuỗi gốc bằng cách sử dụng$ node --experimental-worker test.js { pong: ‘ping’ }
4 sẽ có sẵn trong chuỗi này bằng cách sử dụng$ node --experimental-worker test.js { pong: ‘ping’ }
5$ node --experimental-worker test.js { pong: ‘ping’ }
THÍ DỤ
const { Worker } = require['worker_threads'];
const worker = new Worker[`
const { parentPort } = require['worker_threads'];
parentPort.once['message',
message => parentPort.postMessage[{ pong: message }]];
`, { eval: true }];
worker.on['message', message => console.log[message]];
worker.postMessage['ping'];
$ node --experimental-worker test.js
{ pong: ‘ping’ }
Ví dụ của Anna HenningsenVề cơ bản, điều này làm là tạo một chủ đề mới bằng cách sử dụng Công nhân mới, mã bên trong Công nhân đang lắng nghe một tin nhắn trên
const { Worker } = require['worker_threads'];
const worker = new Worker[`
const { parentPort } = require['worker_threads'];
parentPort.once['message',
message => parentPort.postMessage[{ pong: message }]];
`, { eval: true }];
worker.on['message', message => console.log[message]];
worker.postMessage['ping'];
5 và sau khi nhận được tin nhắn, nó sẽ gửi tin nhắn trở lại chủ đề chínhBạn phải sử dụng
$ node --experimental-worker test.js
{ pong: ‘ping’ }
7 vì Công nhân vẫn đang thử nghiệmMột vi dụ khac
const {
Worker, isMainThread, parentPort, workerData
} = require['worker_threads'];
if [isMainThread] {
module.exports = function parseJSAsync[script] {
return new Promise[[resolve, reject] => {
const worker = new Worker[filename, {
workerData: script
}];
worker.on['message', resolve];
worker.on['error', reject];
worker.on['exit', [code] => {
if [code !== 0]
reject[new Error[`Worker stopped with exit code ${code}`]];
}];
}];
};
} else {
const { parse } = require['some-js-parsing-library'];
const script = workerData;
parentPort.postMessage[parse[script]];
}
Nó yêu cầu
8. lớp đại diện cho một luồng thực thi JavaScript độc lập$ node --experimental-worker test.js { pong: ‘ping’ }
9. một boolean đúng nếu mã không chạy bên trong chuỗi Worker$ node --experimental-worker test.js { pong: ‘ping’ }
5. MessagePort cho phép giao tiếp với luồng gốc Nếu luồng này được sinh ra dưới dạng Công nhânconst { Worker } = require['worker_threads']; const worker = new Worker[` const { parentPort } = require['worker_threads']; parentPort.once['message', message => parentPort.postMessage[{ pong: message }]]; `, { eval: true }]; worker.on['message', message => console.log[message]]; worker.postMessage['ping'];
1. Một giá trị JavaScript tùy ý chứa một bản sao của dữ liệu được truyền tới hàm tạo Worker của luồng nàyconst { Worker, isMainThread, parentPort, workerData } = require['worker_threads']; if [isMainThread] { module.exports = function parseJSAsync[script] { return new Promise[[resolve, reject] => { const worker = new Worker[filename, { workerData: script }]; worker.on['message', resolve]; worker.on['error', reject]; worker.on['exit', [code] => { if [code !== 0] reject[new Error[`Worker stopped with exit code ${code}`]]; }]; }]; }; } else { const { parse } = require['some-js-parsing-library']; const script = workerData; parentPort.postMessage[parse[script]]; }
Trong thực tế đối với các loại nhiệm vụ này, thay vào đó hãy sử dụng nhóm Công nhân. Nếu không, chi phí tạo Công nhân có thể sẽ vượt quá lợi ích của họ
Những gì được mong đợi cho Người lao động [hy vọng]
- Chuyển các tay cầm gốc xung quanh [e. g. ổ cắm, yêu cầu http]
- phát hiện bế tắc. Bế tắc là tình huống trong đó một tập hợp các quy trình bị chặn vì mỗi quy trình đang giữ một tài nguyên và chờ một tài nguyên khác được một số quy trình khác thu được. Việc giữ bế tắc sẽ hữu ích cho các luồng Công nhân trong trường hợp này
- Cô lập hơn, vì vậy nếu một quy trình bị ảnh hưởng, nó sẽ không ảnh hưởng đến các quy trình khác
Điều KHÔNG mong đợi đối với Người lao động
- Đừng nghĩ rằng Công nhân làm mọi thứ nhanh hơn một cách kỳ diệu, trong một số trường hợp, tốt hơn là sử dụng nhóm Công nhân
- Không sử dụng Công nhân để song song hóa các hoạt động I/O
- Đừng nghĩ sinh sản Worker là rẻ
ghi chú cuối cùng
Những người đóng góp cho Công nhân trong Node. js đang tìm kiếm phản hồi, nếu bạn đã sử dụng Worker trước đây và muốn đóng góp, bạn có thể để lại phản hồi của mình tại đây
Công nhân có hỗ trợ Chrome DevTools để kiểm tra Công nhân trong Nút. js
Và worker_threads
là một mô-đun thử nghiệm đầy hứa hẹn nếu bạn cần thực hiện các tác vụ sử dụng nhiều CPU trong Nút của mình. ứng dụng js. Hãy nhớ rằng nó vẫn đang trong quá trình thử nghiệm, vì vậy nên đợi trước khi sử dụng nó trong sản xuất. Hiện tại, bạn có thể sử dụng Worker pool để thay thế