2025-02-05
3 min read

Nodejs Worker Practice

用主執行緒開啟多個子執行緒,然後發送訊息給子執行緒,子執行緒收到後再回傳訊息給主執行緒,主執行緒會等全部都完成才關閉主執行緒,此為參考nodejs官網範例.

實作

代碼:

// nodejs code

import { MessageChannel, MessagePort, BroadcastChannel, Worker, isMainThread, parentPort, threadId } from 'node:worker_threads';
import { fileURLToPath } from 'node:url'
import assert from 'node:assert'

const __filename = fileURLToPath(import.meta.url);

// 檢視當前 process id 與 thread id 以及是否為主執行緒
console.log(`[pid:${process.pid}][tid:${threadId}]isMainThread`, isMainThread, __filename);

if (isMainThread) {
  mainHandler()
} else {
  subHandler()
}

console.log(`[pid:${process.pid}][tid:${threadId}] end`)

async function delay(ms) {
  await new Promise(res => {
    setTimeout(res, ms);
  })
}

// 子執行緒回送訊息給主執行緒
async function subHandler() {
  console.time(`[pid:${process.pid}][tid:${threadId}]sub`)
  console.log(`[pid:${process.pid}][tid:${threadId}]exec sub thread receive msg`);
  // 監聽一次從主執行緒來的訊息,並取的訊息中的MessageChannel port1,透過port1回傳訊息給主執行緒,然後關閉MessageChannel物件
  parentPort.once('message', (value) => {
    console.log(`[pid:${process.pid}][tid:${threadId}] [main sendTime:${Date(value.sendTime)}]sub receive value`, value)
    assert(value.hereIsYourPort instanceof MessagePort);
    value.hereIsYourPort.postMessage('this worker is sending this');
    value.hereIsYourPort.close();
  })
  await delay(10000) // 等10秒,測試是否主執行緒會等子執行緒工作完成後,才結束主執行緒
  console.timeEnd(`[pid:${process.pid}][tid:${threadId}]sub`)
  console.log(`[pid:${process.pid}][tid:${threadId}]sub thread done`)
}

// 主執行緒開啟多個子執行緒並發送送訊息給每個子執行緒
async function mainHandler() {
  const workers = []
  for(let i = 0; i < 100; i++) {
    // await delay(2000)
    const worker = new Worker(__filename);
    const subChannel = new MessageChannel();
    // worker發送訊息並提供port1給子執行緒回傳訊息用
    worker.postMessage({ hereIsYourPort: subChannel.port1, sendTime: Date.now() }, [subChannel.port1]);
    // 主執行緒透過port2間聽從子執行緒送出的訊息
    subChannel.port2.on('message', (value) => {
      console.log(`[pid:${process.pid}][${threadId}] receive:`, value);
    });
    workers.push({
      worker, subChannel
    })
  }

  console.log(`[pid:${process.pid}][tid:${threadId}]main work done`)
}

活動監視器檢視thread與memory使用狀況: screenshot