Worker_threads NodeJS

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

Worker_threads NodeJS

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ác
  • SharedArrayBuffer 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 JavaScript
  • MessagePort, đượ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
  • 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');  
    
    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 nhau
  • 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');  
    
    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ụng
    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');  
    
    2

API

  • 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');  
    
    3 => Lớp
    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');  
    
    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áo
  • 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');  
    
    6 hoặc
    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');  
    
    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ất
  • 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');  
    
    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 nhau
  • $ node --experimental-worker test.js
    { pong: ‘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

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 Henningsen

Về 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ính

Bạn phải sử dụng

$ node --experimental-worker test.js
{ pong: ‘ping’ }
7 vì Công nhân vẫn đang thử nghiệm

Mộ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

  • $ node --experimental-worker test.js
    { pong: ‘ping’ }
    
    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
  • 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. 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ân
  •     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));
        }
    
    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ày

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

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ế

Chuỗi công nhân trong Nodejs là gì?

Trong nút. js, worker thread trở nên hữu ích khi thực hiện các tác vụ JavaScript lớn. Worker giúp đơn giản hóa việc chạy mã Javascript song song bằng cách sử dụng các luồng, giúp quá trình này nhanh hơn và hiệu quả hơn đáng kể . Chúng tôi có thể hoàn thành các công việc khó khăn mà không làm gián đoạn luồng chính.

Chủ đề công nhân là gì?

Luồng công nhân là một luồng song song liên tục chạy và chấp nhận thông báo cho đến khi nó được đóng hoặc kết thúc một cách rõ ràng . Tin nhắn đến một chuỗi công nhân có thể được gửi từ chuỗi cha hoặc chuỗi công nhân con của nó. Xuyên suốt tài liệu này, luồng gốc được gọi là luồng nơi một luồng công nhân được sinh ra.

Sự khác biệt giữa các gói cluster và worker_threads trong nút JS là gì?

Cụm nút. js có thể được sử dụng để chạy nhiều phiên bản của Node. js có thể phân phối khối lượng công việc giữa các luồng ứng dụng của chúng. Khi không cần cách ly quy trình, hãy sử dụng mô-đun worker_threads, cho phép chạy nhiều luồng ứng dụng trong một Nút duy nhất .

Làm cách nào để sử dụng chủ đề trong nodejs?

Nút. js chạy mã JavaScript trong một luồng đơn, nghĩa là mã của bạn chỉ có thể thực hiện một tác vụ tại một thời điểm. Tuy nhiên, Nút. Bản thân js là đa luồng và cung cấp các luồng ẩn thông qua thư viện libuv , xử lý các hoạt động I/O như đọc tệp từ đĩa hoặc yêu cầu mạng.