Skip to content

워커 스레드

[안정됨: 2 - 안정됨]

안정됨: 2 안정성: 2 - 안정됨

소스 코드: lib/worker_threads.js

node:worker_threads 모듈은 JavaScript를 병렬로 실행하는 스레드를 사용할 수 있게 해줍니다. 접근하려면 다음과 같이 하세요.

js
const worker = require('node:worker_threads')

워커(스레드)는 CPU를 많이 사용하는 JavaScript 작업을 수행하는 데 유용합니다. I/O 집약적인 작업에는 그다지 도움이 되지 않습니다. Node.js에 내장된 비동기 I/O 작업이 워커보다 더 효율적입니다.

child_process 또는 cluster와 달리 worker_threads는 메모리를 공유할 수 있습니다. 이를 위해 ArrayBuffer 인스턴스를 전송하거나 SharedArrayBuffer 인스턴스를 공유합니다.

js
const { Worker, isMainThread, parentPort, workerData } = require('node: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))
}

위의 예시는 각 parseJSAsync() 호출에 대해 워커 스레드를 생성합니다. 실제로는 이러한 종류의 작업에 워커 풀을 사용하세요. 그렇지 않으면 워커를 생성하는 오버헤드가 이점보다 클 가능성이 있습니다.

워커 풀을 구현할 때는 AsyncResource API를 사용하여 작업과 결과 간의 상관 관계에 대해 진단 도구(예: 비동기 스택 추적 제공)에 알리세요. 예시 구현은 async_hooks 문서의 "워커 스레드 풀에 AsyncResource 사용"을 참조하세요.

워커 스레드는 기본적으로 프로세스 특정 옵션이 아닌 옵션을 상속합니다. 워커 스레드 옵션, 특히 argvexecArgv 옵션을 사용자 정의하는 방법은 워커 생성자 옵션을 참조하세요.

worker.getEnvironmentData(key)

[히스토리]

버전변경 사항
v17.5.0, v16.15.0더 이상 실험적 기능이 아닙니다.
v15.12.0, v14.18.0추가됨: v15.12.0, v14.18.0
  • key <any> <Map> 키로 사용할 수 있는 임의의 복제 가능한 JavaScript 값입니다.
  • 반환 값: <any>

워커 스레드 내에서 worker.getEnvironmentData()는 스폰된 스레드의 worker.setEnvironmentData()로 전달된 데이터의 복제본을 반환합니다. 모든 새로운 Worker는 환경 데이터의 자체 복사본을 자동으로 받습니다.

js
const { Worker, isMainThread, setEnvironmentData, getEnvironmentData } = require('node:worker_threads')

if (isMainThread) {
  setEnvironmentData('Hello', 'World!')
  const worker = new Worker(__filename)
} else {
  console.log(getEnvironmentData('Hello')) // 'World!'를 출력합니다.
}

worker.isMainThread

추가된 버전: v10.5.0

이 코드가 Worker 스레드 내에서 실행되고 있지 않으면 true입니다.

js
const { Worker, isMainThread } = require('node:worker_threads')

if (isMainThread) {
  // 현재 파일을 Worker 인스턴스 내에서 다시 로드합니다.
  new Worker(__filename)
} else {
  console.log('Inside Worker!')
  console.log(isMainThread) // 'false'를 출력합니다.
}

worker.markAsUntransferable(object)

추가된 버전: v14.5.0, v12.19.0

  • object <any> 임의의 JavaScript 값입니다.

객체를 전송 불가능한 것으로 표시합니다. objectport.postMessage() 호출의 전송 목록에 있으면 오류가 발생합니다. object가 기본값인 경우 이는 no-op입니다.

특히, 이는 전송되는 것이 아니라 복제될 수 있고 보내는 측의 다른 객체에서 사용되는 객체에 적합합니다. 예를 들어, Node.js는 Buffer에 사용하는 ArrayBuffer를 이것으로 표시합니다.

이 작업은 취소할 수 없습니다.

js
const { MessageChannel, markAsUntransferable } = require('node:worker_threads')

const pooledBuffer = new ArrayBuffer(8)
const typedArray1 = new Uint8Array(pooledBuffer)
const typedArray2 = new Float64Array(pooledBuffer)

markAsUntransferable(pooledBuffer)

const { port1 } = new MessageChannel()
try {
  // pooledBuffer는 전송 불가능하므로 오류가 발생합니다.
  port1.postMessage(typedArray1, [typedArray1.buffer])
} catch (error) {
  // error.name === 'DataCloneError'
}

// 다음 줄은 typedArray1의 내용을 출력합니다. 여전히 메모리를 소유하고 있고 전송되지 않았습니다.
// `markAsUntransferable()`이 없으면 빈 Uint8Array를 출력하고 postMessage 호출이 성공합니다.
// typedArray2도 그대로입니다.
console.log(typedArray1)
console.log(typedArray2)

브라우저에는 이 API에 해당하는 것이 없습니다.

worker.isMarkedAsUntransferable(object)

Added in: v21.0.0

markAsUntransferable()로 전달 불가능으로 표시된 객체인지 확인합니다.

js
const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads')

const pooledBuffer = new ArrayBuffer(8)
markAsUntransferable(pooledBuffer)

isMarkedAsUntransferable(pooledBuffer) // true를 반환합니다.

브라우저에는 이 API에 상응하는 것이 없습니다.

worker.markAsUncloneable(object)

Added in: v23.0.0

  • object <any> 임의의 JavaScript 값.

객체를 복제 불가능으로 표시합니다. objectport.postMessage() 호출에서 message로 사용되면 오류가 발생합니다. object가 원시 값인 경우 아무 작업도 수행되지 않습니다.

이는 ArrayBuffer 또는 Buffer와 같은 객체에는 아무런 영향을 미치지 않습니다.

이 작업은 되돌릴 수 없습니다.

js
const { markAsUncloneable } = require('node:worker_threads')

const anyObject = { foo: 'bar' }
markAsUncloneable(anyObject)
const { port1 } = new MessageChannel()
try {
  // anyObject는 복제할 수 없으므로 이 코드는 오류를 발생시킵니다.
  port1.postMessage(anyObject)
} catch (error) {
  // error.name === 'DataCloneError'
}

브라우저에는 이 API에 상응하는 것이 없습니다.

worker.moveMessagePortToContext(port, contextifiedSandbox)

Added in: v11.13.0

MessagePort를 다른 vm 컨텍스트로 전송합니다. 원래 port 객체는 사용할 수 없게 되고 반환된 MessagePort 인스턴스가 대신 사용됩니다.

반환된 MessagePort는 대상 컨텍스트의 객체이며 해당 전역 Object 클래스에서 상속됩니다. port.onmessage() 리스너에 전달된 객체도 대상 컨텍스트에서 생성되고 해당 전역 Object 클래스에서 상속됩니다.

그러나 생성된 MessagePort는 더 이상 EventTarget에서 상속되지 않으며, port.onmessage()만 사용하여 이벤트를 받을 수 있습니다.

worker.parentPort

추가된 버전: v10.5.0

이 스레드가 Worker인 경우, 이것은 부모 스레드와 통신할 수 있는 MessagePort입니다. parentPort.postMessage()를 사용하여 보낸 메시지는 worker.on('message')를 사용하여 부모 스레드에서 사용할 수 있으며, 부모 스레드에서 worker.postMessage()를 사용하여 보낸 메시지는 parentPort.on('message')를 사용하여 이 스레드에서 사용할 수 있습니다.

js
const { Worker, isMainThread, parentPort } = require('node:worker_threads')

if (isMainThread) {
  const worker = new Worker(__filename)
  worker.once('message', message => {
    console.log(message) // 'Hello, world!'를 출력합니다.
  })
  worker.postMessage('Hello, world!')
} else {
  // 부모 스레드에서 메시지를 받으면 다시 보냅니다.
  parentPort.once('message', message => {
    parentPort.postMessage(message)
  })
}

worker.postMessageToThread(threadId, value[, transferList][, timeout])

추가된 버전: v22.5.0

[안정성: 1 - 실험적]

안정성: 1 안정성: 1.1 - 활발한 개발 중

  • threadId <number> 대상 스레드 ID입니다. 스레드 ID가 유효하지 않으면 ERR_WORKER_MESSAGING_FAILED 오류가 발생합니다. 대상 스레드 ID가 현재 스레드 ID이면 ERR_WORKER_MESSAGING_SAME_THREAD 오류가 발생합니다.
  • value <any> 보낼 값입니다.
  • transferList <Object[]> 하나 이상의 MessagePort와 유사한 객체가 value에 전달되는 경우, 해당 항목에 대한 transferList가 필요하며, 그렇지 않으면 ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST가 발생합니다. 자세한 내용은 port.postMessage()를 참조하십시오.
  • timeout <number> 메시지가 전달될 때까지 기다리는 시간(밀리초)입니다. 기본적으로 undefined이며, 이는 무한정 대기한다는 의미입니다. 작업 시간이 초과되면 ERR_WORKER_MESSAGING_TIMEOUT 오류가 발생합니다.
  • 반환값: <Promise> 메시지가 대상 스레드에 의해 성공적으로 처리된 경우 이행되는 프로미스입니다.

스레드 ID로 식별되는 다른 워커에게 값을 보냅니다.

대상 스레드에 workerMessage 이벤트에 대한 리스너가 없으면 작업이 ERR_WORKER_MESSAGING_FAILED 오류를 발생시킵니다.

대상 스레드가 workerMessage 이벤트를 처리하는 동안 오류를 발생시키면 작업이 ERR_WORKER_MESSAGING_ERRORED 오류를 발생시킵니다.

이 메서드는 대상 스레드가 현재 스레드의 직계 부모 또는 자식이 아닌 경우에 사용해야 합니다. 두 스레드가 부모-자식 관계인 경우, require('node:worker_threads').parentPort.postMessage()worker.postMessage()를 사용하여 스레드가 통신하도록 합니다.

아래 예제는 postMessageToThread의 사용법을 보여줍니다. 10개의 중첩된 스레드를 생성하고, 마지막 스레드가 메인 스레드와 통신하려고 합니다.

js
import { fileURLToPath } from 'node:url'
import process from 'node:process'
import { postMessageToThread, threadId, workerData, Worker } from 'node:worker_threads'

const channel = new BroadcastChannel('sync')
const level = workerData?.level ?? 0

if (level < 10) {
  const worker = new Worker(fileURLToPath(import.meta.url), {
    workerData: { level: level + 1 },
  })
}

if (level === 0) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    postMessageToThread(source, { message: 'pong' })
  })
} else if (level === 10) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    channel.postMessage('done')
    channel.close()
  })

  await postMessageToThread(0, { message: 'ping' })
}

channel.onmessage = channel.close
js
const { postMessageToThread, threadId, workerData, Worker } = require('node:worker_threads')

const channel = new BroadcastChannel('sync')
const level = workerData?.level ?? 0

if (level < 10) {
  const worker = new Worker(__filename, {
    workerData: { level: level + 1 },
  })
}

if (level === 0) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    postMessageToThread(source, { message: 'pong' })
  })
} else if (level === 10) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    channel.postMessage('done')
    channel.close()
  })

  postMessageToThread(0, { message: 'ping' })
}

channel.onmessage = channel.close

worker.receiveMessageOnPort(port)

[기록]

버전변경 사항
v15.12.0이제 port 인수는 BroadcastChannel을 참조할 수도 있습니다.
v12.3.0v12.3.0에 추가됨

주어진 MessagePort에서 단일 메시지를 수신합니다. 사용 가능한 메시지가 없으면 undefined가 반환되고, 그렇지 않으면 MessagePort의 큐에서 가장 오래된 메시지에 해당하는 메시지 페이로드를 포함하는 단일 message 속성을 가진 객체가 반환됩니다.

js
const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
port1.postMessage({ hello: 'world' })

console.log(receiveMessageOnPort(port2))
// 출력: { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2))
// 출력: undefined

이 함수를 사용하면 'message' 이벤트가 발생하지 않으며 onmessage 리스너가 호출되지 않습니다.

worker.resourceLimits

추가된 버전: v13.2.0, v12.16.0

이 워커 스레드 내의 JS 엔진 리소스 제약 조건 집합을 제공합니다. resourceLimits 옵션이 Worker 생성자에 전달된 경우 이는 해당 값과 일치합니다.

메인 스레드에서 사용되는 경우 이 값은 빈 객체입니다.

worker.SHARE_ENV

추가됨: v11.14.0

현재 스레드와 Worker 스레드가 동일한 환경 변수 집합에 대한 읽기 및 쓰기 액세스를 공유해야 함을 나타내기 위해 Worker 생성자의 env 옵션으로 전달할 수 있는 특수 값입니다.

js
const { Worker, SHARE_ENV } = require('node:worker_threads')
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV }).on('exit', () => {
  console.log(process.env.SET_IN_WORKER) // 'foo'를 출력합니다.
})

worker.setEnvironmentData(key[, value])

[기록]

버전변경 사항
v17.5.0, v16.15.0더 이상 실험적이지 않습니다.
v15.12.0, v14.18.0추가됨: v15.12.0, v14.18.0
  • key <any> <Map> 키로 사용할 수 있는 임의의 복제 가능한 JavaScript 값입니다.
  • value <any> 복제되어 모든 새 Worker 인스턴스에 자동으로 전달되는 임의의 복제 가능한 JavaScript 값입니다. valueundefined로 전달되면 key에 대해 이전에 설정된 값이 삭제됩니다.

worker.setEnvironmentData() API는 현재 스레드와 현재 컨텍스트에서 생성된 모든 새 Worker 인스턴스에서 worker.getEnvironmentData()의 내용을 설정합니다.

worker.threadId

추가됨: v10.5.0

현재 스레드의 정수 식별자입니다. 해당 워커 객체(있는 경우)에서는 worker.threadId로 사용할 수 있습니다. 이 값은 단일 프로세스 내의 각 Worker 인스턴스에 대해 고유합니다.

worker.workerData

추가된 버전: v10.5.0

이 스레드의 Worker 생성자에 전달된 데이터의 클론을 포함하는 임의의 JavaScript 값입니다.

데이터는 HTML 구조적 클론 알고리즘에 따라 postMessage()를 사용하는 것처럼 클론됩니다.

js
const { Worker, isMainThread, workerData } = require('node:worker_threads')

if (isMainThread) {
  const worker = new Worker(__filename, { workerData: 'Hello, world!' })
} else {
  console.log(workerData) // 'Hello, world!'를 출력합니다.
}

클래스: BroadcastChannel extends EventTarget

[기록]

버전변경 사항
v18.0.0더 이상 실험적이지 않음.
v15.4.0추가된 버전: v15.4.0

BroadcastChannel 인스턴스는 동일한 채널 이름에 바인딩된 다른 모든 BroadcastChannel 인스턴스와 비동기 일대다 통신을 할 수 있도록 합니다.

js
'use strict'

const { isMainThread, BroadcastChannel, Worker } = require('node:worker_threads')

const bc = new BroadcastChannel('hello')

if (isMainThread) {
  let c = 0
  bc.onmessage = event => {
    console.log(event.data)
    if (++c === 10) bc.close()
  }
  for (let n = 0; n < 10; n++) new Worker(__filename)
} else {
  bc.postMessage('hello from every worker')
  bc.close()
}

new BroadcastChannel(name)

추가된 버전: v15.4.0

  • name <any> 연결할 채널의 이름입니다. ${name}을 사용하여 문자열로 변환할 수 있는 모든 JavaScript 값이 허용됩니다.

broadcastChannel.close()

추가된 버전: v15.4.0

BroadcastChannel 연결을 닫습니다.

broadcastChannel.onmessage

추가된 버전: v15.4.0

  • 유형: <Function> 메시지를 수신하면 단일 MessageEvent 인수로 호출됩니다.

broadcastChannel.onmessageerror

추가된 버전: v15.4.0

  • 유형: <Function> 수신된 메시지를 역직렬화할 수 없을 때 호출됩니다.

broadcastChannel.postMessage(message)

추가된 버전: v15.4.0

  • message <any> 복제 가능한 JavaScript 값입니다.

broadcastChannel.ref()

추가된 버전: v15.4.0

unref()의 반대입니다. 이전에 unref()된 BroadcastChannel에서 ref()를 호출해도 프로그램이 유일한 활성 핸들이 남았을 때 종료되지 않습니다(기본 동작). 포트가 ref()된 경우, ref()를 다시 호출해도 아무런 효과가 없습니다.

broadcastChannel.unref()

추가된 버전: v15.4.0

BroadcastChannel에서 unref()를 호출하면 이벤트 시스템에서 유일한 활성 핸들인 경우 스레드가 종료될 수 있습니다. BroadcastChannel이 이미 unref()된 경우 unref()를 다시 호출해도 아무런 효과가 없습니다.

클래스: MessageChannel

추가된 버전: v10.5.0

worker.MessageChannel 클래스의 인스턴스는 비동기 양방향 통신 채널을 나타냅니다. MessageChannel에는 자체 메서드가 없습니다. new MessageChannel()은 연결된 MessagePort 인스턴스를 참조하는 port1port2 속성을 가진 객체를 생성합니다.

js
const { MessageChannel } = require('node:worker_threads')

const { port1, port2 } = new MessageChannel()
port1.on('message', message => console.log('received', message))
port2.postMessage({ foo: 'bar' })
// `port1.on('message')` 리스너에서: received { foo: 'bar' } 출력

클래스: MessagePort

[기록]

버전변경 사항
v14.7.0이제 이 클래스는 EventEmitter가 아닌 EventTarget에서 상속합니다.
v10.5.0추가된 버전: v10.5.0

worker.MessagePort 클래스의 인스턴스는 비동기 양방향 통신 채널의 한쪽 끝을 나타냅니다. 서로 다른 Worker 간에 구조화된 데이터, 메모리 영역 및 기타 MessagePort를 전송하는 데 사용할 수 있습니다.

이 구현은 브라우저 MessagePort와 일치합니다.

Event: 'close'

추가된 버전: v10.5.0

채널의 한쪽이 연결 해제되면 한 번 'close' 이벤트가 발생합니다.

js
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()

// 출력:
//   foobar
//   closed!
port2.on('message', message => console.log(message))
port2.on('close', () => console.log('closed!'))

port1.postMessage('foobar')
port1.close()

Event: 'message'

추가된 버전: v10.5.0

  • value <any> 전송된 값

'message' 이벤트는 port.postMessage()의 복제된 입력을 포함하는 모든 들어오는 메시지에 대해 발생합니다.

이 이벤트의 리스너는 postMessage()에 전달된 value 매개변수의 복제본과 다른 추가 인수를 받지 않습니다.

Event: 'messageerror'

추가된 버전: v14.5.0, v12.19.0

메시지 역직렬화에 실패하면 'messageerror' 이벤트가 발생합니다.

현재 이 이벤트는 수신 측에서 게시된 JS 객체를 인스턴스화하는 중에 오류가 발생할 때 발생합니다. 이러한 상황은 드물지만 예를 들어 특정 Node.js API 객체가 vm.Context에서 수신될 때(현재 Node.js API를 사용할 수 없는 경우) 발생할 수 있습니다.

port.close()

추가된 버전: v10.5.0

연결의 양쪽에서 메시지를 더 이상 보낼 수 없게 합니다. 이 메서드는 이 MessagePort를 통해 더 이상 통신이 발생하지 않을 때 호출할 수 있습니다.

'close' 이벤트는 채널의 일부인 두 MessagePort 인스턴스에서 발생합니다.

port.postMessage(value[, transferList])

[기록]

버전변경 사항
v21.0.0전송 불가능한 객체가 전송 목록에 있으면 오류가 발생합니다.
v15.6.0복제 가능한 유형 목록에 X509Certificate가 추가되었습니다.
v15.0.0복제 가능한 유형 목록에 CryptoKey가 추가되었습니다.
v15.14.0, v14.18.0복제 가능한 유형 목록에 'BlockList'가 추가되었습니다.
v15.9.0, v14.18.0복제 가능한 유형 목록에 'Histogram' 유형이 추가되었습니다.
v14.5.0, v12.19.0복제 가능한 유형 목록에 KeyObject가 추가되었습니다.
v14.5.0, v12.19.0전송 가능한 유형 목록에 FileHandle이 추가되었습니다.
v10.5.0추가된 버전: v10.5.0

이 채널의 수신 측에 JavaScript 값을 보냅니다. valueHTML 구조적 복제 알고리즘과 호환되는 방식으로 전송됩니다.

특히, JSON과의 중요한 차이점은 다음과 같습니다.

js
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()

port1.on('message', message => console.log(message))

const circularData = {}
circularData.foo = circularData
// 출력: { foo: [Circular] }
port2.postMessage(circularData)

transferListArrayBuffer, MessagePortFileHandle 객체 목록일 수 있습니다. 전송 후에는 ( value에 포함되어 있지 않더라도) 채널의 발신 측에서 더 이상 사용할 수 없습니다. 하위 프로세스와 달리 현재 네트워크 소켓과 같은 핸들 전송은 지원되지 않습니다.

valueSharedArrayBuffer 인스턴스가 포함된 경우 해당 인스턴스는 어느 스레드에서든 액세스할 수 있습니다. transferList에 나열할 수 없습니다.

value에는 transferList에 없는 ArrayBuffer 인스턴스가 여전히 포함될 수 있습니다. 이 경우 기본 메모리는 이동되지 않고 복사됩니다.

js
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()

port1.on('message', message => console.log(message))

const uint8Array = new Uint8Array([1, 2, 3, 4])
// 이것은 `uint8Array`의 복사본을 게시합니다.
port2.postMessage(uint8Array)
// 이것은 데이터를 복사하지 않고 `uint8Array`를 사용할 수 없게 만듭니다.
port2.postMessage(uint8Array, [uint8Array.buffer])

// `sharedUint8Array`의 메모리는 `.on('message')`에서 받은 원본과 복사본 모두에서 액세스할 수 있습니다.
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4))
port2.postMessage(sharedUint8Array)

// 이것은 새로 생성된 메시지 포트를 수신자에게 전송합니다.
// 예를 들어, 같은 부모 스레드의 하위 스레드인 여러 `Worker` 스레드 간에
// 통신 채널을 만드는 데 사용할 수 있습니다.
const otherChannel = new MessageChannel()
port2.postMessage({ port: otherChannel.port1 }, [otherChannel.port1])

메시지 객체는 즉시 복제되며, 게시 후 부작용 없이 수정할 수 있습니다.

이 API의 기반이 되는 직렬화 및 역직렬화 메커니즘에 대한 자세한 내용은 node:v8 모듈의 직렬화 API를 참조하십시오.

TypedArray 및 버퍼 전송 시 고려 사항

모든 TypedArrayBuffer 인스턴스는 기본 ArrayBuffer에 대한 뷰입니다. 즉, 원시 데이터를 실제로 저장하는 것은 ArrayBuffer이며, TypedArrayBuffer 객체는 데이터를 보고 조작하는 방법을 제공합니다. 동일한 ArrayBuffer 인스턴스에 대해 여러 개의 뷰를 만드는 것이 가능하며 일반적입니다. 전송 목록을 사용하여 ArrayBuffer를 전송할 때 매우 주의해야 합니다. 이렇게 하면 동일한 ArrayBuffer를 공유하는 모든 TypedArrayBuffer 인스턴스를 사용할 수 없게 됩니다.

js
const ab = new ArrayBuffer(10)

const u1 = new Uint8Array(ab)
const u2 = new Uint16Array(ab)

console.log(u2.length) // 5 출력

port.postMessage(u1, [u1.buffer])

console.log(u2.length) // 0 출력

특히 Buffer 인스턴스의 경우, 기본 ArrayBuffer를 전송하거나 복제할 수 있는지 여부는 인스턴스가 생성된 방식에 따라 다르며, 이는 신뢰성 있게 확인할 수 없는 경우가 많습니다.

ArrayBuffer는 항상 복제되어야 하고 절대 전송되지 않아야 함을 나타내기 위해 markAsUntransferable()로 표시될 수 있습니다.

Buffer 인스턴스가 생성된 방식에 따라 기본 ArrayBuffer를 소유할 수도 있고 그렇지 않을 수도 있습니다. Buffer 인스턴스가 소유한 것으로 알려진 경우가 아니면 ArrayBuffer를 전송해서는 안 됩니다. 특히 내부 Buffer 풀에서 생성된 Buffer(예: Buffer.from() 또는 Buffer.allocUnsafe() 사용)의 경우, 전송이 불가능하며 항상 복제되어 전체 Buffer 풀의 복사본을 보냅니다. 이 동작은 의도치 않은 높은 메모리 사용량과 잠재적인 보안 문제를 야기할 수 있습니다.

Buffer 풀링에 대한 자세한 내용은 Buffer.allocUnsafe()를 참조하세요.

Buffer.alloc() 또는 Buffer.allocUnsafeSlow()를 사용하여 생성된 Buffer 인스턴스의 ArrayBuffer는 항상 전송할 수 있지만, 그렇게 하면 해당 ArrayBuffer의 다른 모든 기존 뷰를 사용할 수 없게 됩니다.

프로토타입, 클래스 및 접근자를 사용하는 객체 복제 시 고려 사항

객체 복제는 HTML 구조 복제 알고리즘을 사용하기 때문에 열거할 수 없는 속성, 속성 접근자 및 객체 프로토타입은 유지되지 않습니다. 특히, Buffer 객체는 수신 측에서 일반 Uint8Array로 읽혀지고, JavaScript 클래스의 인스턴스는 일반 JavaScript 객체로 복제됩니다.

js
const b = Symbol('b')

class Foo {
  #a = 1
  constructor() {
    this[b] = 2
    this.c = 3
  }

  get d() {
    return 4
  }
}

const { port1, port2 } = new MessageChannel()

port1.onmessage = ({ data }) => console.log(data)

port2.postMessage(new Foo())

// 출력: { c: 3 }

이 제한은 전역 URL 객체와 같은 많은 내장 객체로 확장됩니다.

js
const { port1, port2 } = new MessageChannel()

port1.onmessage = ({ data }) => console.log(data)

port2.postMessage(new URL('https://example.org'))

// 출력: { }

port.hasRef()

추가됨: v18.1.0, v16.17.0

[안정됨: 1 - 실험적]

안정됨: 1 안정성: 1 - 실험적

true인 경우 MessagePort 객체는 Node.js 이벤트 루프를 활성 상태로 유지합니다.

port.ref()

추가됨: v10.5.0

unref()의 반대입니다. 이전에 unref()된 포트에서 ref()를 호출해도 활성 핸들이 하나만 남아 있는 경우 프로그램이 종료되지 않습니다(기본 동작). 포트가 ref()된 경우 ref()를 다시 호출해도 아무런 영향이 없습니다.

.on('message')를 사용하여 리스너를 추가하거나 제거하면 이벤트에 대한 리스너의 존재 여부에 따라 포트가 자동으로 ref()unref()됩니다.

port.start()

추가된 버전: v10.5.0

MessagePort에서 메시지 수신을 시작합니다. 이 포트를 이벤트 이미터로 사용하는 경우, 'message' 리스너가 첨부되면 자동으로 호출됩니다.

이 메서드는 웹 MessagePort API와의 패리티를 위해 존재합니다. Node.js에서는 이벤트 리스너가 없는 경우 메시지를 무시하는 데만 유용합니다. Node.js는 또한 .onmessage의 처리 방식에서 차이가 있습니다. 이것을 설정하면 자동으로 .start()가 호출되지만, 설정 해제하면 새로운 핸들러가 설정되거나 포트가 폐기될 때까지 메시지가 대기열에 추가됩니다.

port.unref()

추가된 버전: v10.5.0

포트에서 unref()를 호출하면 이벤트 시스템에서 유일하게 활성 핸들인 경우 스레드가 종료될 수 있습니다. 포트가 이미 unref()된 경우 unref()를 다시 호출해도 아무런 효과가 없습니다.

.on('message')를 사용하여 리스너를 첨부하거나 제거하는 경우, 이벤트에 대한 리스너가 존재하는지 여부에 따라 포트가 자동으로 ref()unref()됩니다.

클래스: Worker

추가된 버전: v10.5.0

Worker 클래스는 독립적인 JavaScript 실행 스레드를 나타냅니다. 대부분의 Node.js API는 내부에서 사용할 수 있습니다.

워커 환경 내부의 주목할 만한 차이점은 다음과 같습니다.

  • process.stdin, process.stdout, 및 process.stderr 스트림은 부모 스레드에서 리디렉션될 수 있습니다.
  • require('node:worker_threads').isMainThread 속성이 false로 설정됩니다.
  • require('node:worker_threads').parentPort 메시지 포트를 사용할 수 있습니다.
  • process.exit()는 전체 프로그램이 아닌 단일 스레드만 중지하며, process.abort()는 사용할 수 없습니다.
  • process.chdir() 및 그룹 또는 사용자 ID를 설정하는 process 메서드를 사용할 수 없습니다.
  • process.env는 다른 방식으로 지정되지 않은 경우 부모 스레드의 환경 변수 복사본입니다. 한 복사본의 변경 사항은 다른 스레드에서 볼 수 없으며, 네이티브 애드온에는 표시되지 않습니다 (worker.SHARE_ENVWorker 생성자에 env 옵션으로 전달되지 않은 경우). Windows에서는 메인 스레드와 달리 환경 변수 복사본이 대소문자를 구분하여 작동합니다.
  • process.title은 수정할 수 없습니다.
  • 신호는 process.on('...')를 통해 전달되지 않습니다.
  • 실행은 worker.terminate()가 호출된 결과로 언제든지 중지될 수 있습니다.
  • 부모 프로세스의 IPC 채널에 접근할 수 없습니다.
  • trace_events 모듈은 지원되지 않습니다.
  • 네이티브 애드온은 특정 조건을 충족하는 경우에만 여러 스레드에서 로드할 수 있습니다.

다른 Worker 내에서 Worker 인스턴스를 생성할 수 있습니다.

웹 워커node:cluster 모듈과 마찬가지로 스레드 간 메시지 전달을 통해 양방향 통신을 달성할 수 있습니다. 내부적으로 WorkerWorker가 생성될 때 이미 서로 연결된 내장 MessagePort 쌍을 가지고 있습니다. 부모 측의 MessagePort 객체는 직접 노출되지는 않지만, 해당 기능은 부모 스레드의 Worker 객체에서 worker.postMessage()worker.on('message') 이벤트를 통해 노출됩니다.

사용자 정의 메시징 채널(관심사의 분리를 용이하게 하므로 기본 글로벌 채널을 사용하는 것보다 권장됨)을 생성하기 위해 사용자는 어느 스레드에서든 MessageChannel 객체를 생성하고 기존 채널(예: 글로벌 채널)을 통해 해당 MessageChannelMessagePort 중 하나를 다른 스레드로 전달할 수 있습니다.

메시지가 전달되는 방법과 스레드 경계를 통해 성공적으로 전송할 수 있는 JavaScript 값 유형에 대한 자세한 내용은 port.postMessage()를 참조하십시오.

js
const assert = require('node:assert')
const { Worker, MessageChannel, MessagePort, isMainThread, parentPort } = require('node:worker_threads')
if (isMainThread) {
  const worker = new Worker(__filename)
  const subChannel = new MessageChannel()
  worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1])
  subChannel.port2.on('message', value => {
    console.log('received:', value)
  })
} else {
  parentPort.once('message', value => {
    assert(value.hereIsYourPort instanceof MessagePort)
    value.hereIsYourPort.postMessage('the worker is sending this')
    value.hereIsYourPort.close()
  })
}

new Worker(filename[, options])

[History]

버전변경 사항
v19.8.0, v18.16.0디버깅을 위해 워커 제목에 이름을 추가할 수 있는 name 옵션 지원을 추가했습니다.
v14.9.0filename 매개변수는 data: 프로토콜을 사용하는 WHATWG URL 객체일 수 있습니다.
v14.9.0trackUnmanagedFds 옵션이 기본적으로 true로 설정되었습니다.
v14.6.0, v12.19.0trackUnmanagedFds 옵션이 도입되었습니다.
v13.13.0, v12.17.0transferList 옵션이 도입되었습니다.
v13.12.0, v12.17.0filename 매개변수는 file: 프로토콜을 사용하는 WHATWG URL 객체일 수 있습니다.
v13.4.0, v12.16.0argv 옵션이 도입되었습니다.
v13.2.0, v12.16.0resourceLimits 옵션이 도입되었습니다.
v10.5.0v10.5.0에서 추가됨
  • filename <string> | <URL> 워커의 메인 스크립트 또는 모듈의 경로입니다. 절대 경로이거나 ./ 또는 ../로 시작하는 현재 작업 디렉토리(current working directory)를 기준으로 하는 상대 경로여야 합니다. 또는 file: 또는 data: 프로토콜을 사용하는 WHATWG URL 객체여야 합니다. data: URL을 사용하는 경우, 데이터는 ECMAScript 모듈 로더를 사용하여 MIME 유형에 따라 해석됩니다. options.evaltrue이면, 이 값은 경로가 아닌 JavaScript 코드를 포함하는 문자열입니다.
  • options <Object>
    • argv <any[]> 워커에서 문자열화되어 process.argv에 추가되는 인수 목록입니다. 이는 workerData와 매우 유사하지만 값은 스크립트에 CLI 옵션으로 전달된 것처럼 전역 process.argv에서 사용할 수 있습니다.
    • env <Object> 설정된 경우, 워커 스레드 내부의 process.env의 초기 값을 지정합니다. 특수 값으로, worker.SHARE_ENV를 사용하여 부모 스레드와 자식 스레드가 환경 변수를 공유해야 함을 지정할 수 있습니다. 이 경우, 한 스레드의 process.env 객체에 대한 변경 사항이 다른 스레드에도 영향을 줍니다. 기본값: process.env.
    • eval <boolean> true이고 첫 번째 인수가 string인 경우, 워커가 온라인 상태가 되면 실행되는 스크립트로 생성자의 첫 번째 인수를 해석합니다.
    • execArgv <string[]> 워커에 전달되는 node CLI 옵션 목록입니다. V8 옵션(예: --max-old-space-size) 및 프로세스에 영향을 주는 옵션(예: --title)은 지원되지 않습니다. 설정된 경우, 이는 워커 내부에서 process.execArgv로 제공됩니다. 기본적으로 옵션은 부모 스레드에서 상속됩니다.
    • stdin <boolean> true로 설정된 경우, worker.stdin은 워커 내부의 process.stdin으로 나타나는 내용을 담은 쓰기 가능한 스트림을 제공합니다. 기본적으로 데이터는 제공되지 않습니다.
    • stdout <boolean> true로 설정된 경우, worker.stdout은 부모의 process.stdout으로 자동 파이프되지 않습니다.
    • stderr <boolean> true로 설정된 경우, worker.stderr은 부모의 process.stderr로 자동 파이프되지 않습니다.
    • workerData <any> 복제되어 require('node:worker_threads').workerData로 사용할 수 있는 모든 JavaScript 값입니다. 복제는 HTML 구조화된 복제 알고리즘에 설명된 대로 발생하며, 객체를 복제할 수 없으면(예: function을 포함하는 경우) 오류가 발생합니다.
    • trackUnmanagedFds <boolean> true로 설정된 경우, 워커는 fs.open()fs.close()를 통해 관리되는 원시 파일 디스크립터를 추적하고, 네트워크 소켓 또는 FileHandle API를 통해 관리되는 파일 디스크립터와 유사하게 워커가 종료될 때 닫습니다. 이 옵션은 모든 중첩된 Worker에서 자동으로 상속됩니다. 기본값: true.
    • transferList <Object[]> 하나 이상의 MessagePort와 유사한 객체가 workerData에 전달되는 경우, 해당 항목에 대해 transferList가 필요하거나 ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST가 발생합니다. 자세한 내용은 port.postMessage()를 참조하십시오.
    • resourceLimits <Object> 새 JS 엔진 인스턴스에 대한 선택적 리소스 제한 집합입니다. 이러한 제한에 도달하면 Worker 인스턴스가 종료됩니다. 이러한 제한은 JS 엔진에만 영향을 미치며, ArrayBuffer를 포함한 외부 데이터에는 영향을 미치지 않습니다. 이러한 제한이 설정된 경우에도, 전역 메모리 부족 상황이 발생하면 프로세스가 중단될 수 있습니다.
    • maxOldGenerationSizeMb <number> 메인 힙의 최대 크기(MB)입니다. 명령줄 인수 --max-old-space-size가 설정된 경우, 이 설정이 재정의됩니다.
    • maxYoungGenerationSizeMb <number> 최근에 생성된 객체에 대한 힙 공간의 최대 크기입니다. 명령줄 인수 --max-semi-space-size가 설정된 경우, 이 설정이 재정의됩니다.
    • codeRangeSizeMb <number> 생성된 코드에 사용되는 미리 할당된 메모리 범위의 크기입니다.
    • stackSizeMb <number> 스레드의 기본 최대 스택 크기입니다. 값이 작으면 워커 인스턴스를 사용할 수 없게 될 수 있습니다. 기본값: 4.
    • name <string> 디버깅/식별 목적으로 워커 제목에 추가할 선택적 name으로, 최종 제목은 [worker ${id}] ${name}이 됩니다. 기본값: ''.

이벤트: 'error'

추가된 버전: v10.5.0

워커 스레드에서 처리되지 않은 예외가 발생하면 'error' 이벤트가 발생합니다. 이 경우 워커는 종료됩니다.

이벤트: 'exit'

추가된 버전: v10.5.0

워커가 중지되면 'exit' 이벤트가 발생합니다. 워커가 process.exit()를 호출하여 종료된 경우 exitCode 매개변수는 전달된 종료 코드입니다. 워커가 종료된 경우 exitCode 매개변수는 1입니다.

이것은 모든 Worker 인스턴스에서 발생하는 마지막 이벤트입니다.

이벤트: 'message'

추가된 버전: v10.5.0

  • value <any> 전송된 값

워커 스레드가 require('node:worker_threads').parentPort.postMessage()를 호출하면 'message' 이벤트가 발생합니다. 자세한 내용은 port.on('message') 이벤트를 참조하십시오.

워커 스레드에서 보낸 모든 메시지는 Worker 객체에서 'exit' 이벤트가 발생하기 전에 발생합니다.

이벤트: 'messageerror'

추가된 버전: v14.5.0, v12.19.0

메시지 역직렬화에 실패하면 'messageerror' 이벤트가 발생합니다.

이벤트: 'online'

추가된 버전: v10.5.0

워커 스레드가 JavaScript 코드 실행을 시작하면 'online' 이벤트가 발생합니다.

worker.getHeapSnapshot([options])

[기록]

버전변경 사항
v19.1.0힙 스냅샷을 구성하는 옵션을 지원합니다.
v13.9.0, v12.17.0추가됨: v13.9.0, v12.17.0
  • options <Object>

    • exposeInternals <boolean> true이면 힙 스냅샷에 내부 정보를 노출합니다. 기본값: false.
    • exposeNumericValues <boolean> true이면 인공 필드에 숫자 값을 노출합니다. 기본값: false.
  • 반환 값: <Promise> V8 힙 스냅샷을 포함하는 읽기 가능한 스트림에 대한 프로미스

Worker의 현재 상태의 V8 스냅샷에 대한 읽기 가능한 스트림을 반환합니다. 자세한 내용은 v8.getHeapSnapshot()를 참조하십시오.

'exit' 이벤트가 발생하기 전에 발생할 수 있는 워커 스레드가 더 이상 실행 중이지 않으면 반환된 PromiseERR_WORKER_NOT_RUNNING 오류와 함께 즉시 거부됩니다.

worker.performance

추가된 버전: v15.1.0, v14.17.0, v12.22.0

워커 인스턴스에서 성능 정보를 쿼리하는 데 사용할 수 있는 객체입니다. perf_hooks.performance와 유사합니다.

performance.eventLoopUtilization([utilization1[, utilization2]])

추가된 버전: v15.1.0, v14.17.0, v12.22.0

  • utilization1 <Object> eventLoopUtilization()에 대한 이전 호출 결과입니다.
  • utilization2 <Object> utilization1 이전의 eventLoopUtilization()에 대한 이전 호출 결과입니다.
  • 반환: <Object>

워커 인스턴스의 값이 반환된다는 점을 제외하고는 perf_hooks eventLoopUtilization()과 동일한 호출입니다.

한 가지 차이점은 메인 스레드와 달리 워커 내 부트스트래핑이 이벤트 루프 내에서 수행된다는 것입니다. 따라서 워커의 스크립트 실행이 시작되면 이벤트 루프 활용률을 즉시 사용할 수 있습니다.

증가하지 않는 idle 시간은 워커가 부트스트랩에 갇혀 있음을 나타내지 않습니다. 다음 예에서는 워커의 전체 수명이 idle 시간을 전혀 축적하지 않지만 여전히 메시지를 처리할 수 있는 방법을 보여줍니다.

js
const { Worker, isMainThread, parentPort } = require('node:worker_threads')

if (isMainThread) {
  const worker = new Worker(__filename)
  setInterval(() => {
    worker.postMessage('hi')
    console.log(worker.performance.eventLoopUtilization())
  }, 100).unref()
  return
}

parentPort.on('message', () => console.log('msg')).unref()
;(function r(n) {
  if (--n < 0) return
  const t = Date.now()
  while (Date.now() - t < 300);
  setImmediate(r, n)
})(10)

워커의 이벤트 루프 활용률은 'online' 이벤트가 발생한 후에만 사용할 수 있으며, 이전에 호출하거나 'exit' 이벤트 이후에 호출하면 모든 속성의 값이 0입니다.

worker.postMessage(value[, transferList])

추가된 버전: v10.5.0

require('node:worker_threads').parentPort.on('message')를 통해 받는 워커에게 메시지를 보냅니다. 자세한 내용은 port.postMessage()를 참조하세요.

worker.ref()

추가된 버전: v10.5.0

unref()의 반대입니다. 이전에 unref()된 워커에서 ref()를 호출하면 유일하게 활성 핸들로 남은 경우에도 프로그램이 종료되지 않습니다(기본 동작). 워커가 ref()된 경우, ref()를 다시 호출해도 효과가 없습니다.

worker.resourceLimits

추가된 버전: v13.2.0, v12.16.0

이 Worker 스레드에 대한 JS 엔진 리소스 제약 조건 세트를 제공합니다. resourceLimits 옵션이 Worker 생성자에 전달된 경우, 해당 값과 일치합니다.

워커가 중지된 경우 반환 값은 빈 객체입니다.

worker.stderr

추가된 버전: v10.5.0

이것은 워커 스레드 내부에서 process.stderr에 기록된 데이터를 포함하는 읽기 가능한 스트림입니다. stderr: trueWorker 생성자에 전달되지 않은 경우, 데이터는 부모 스레드의 process.stderr 스트림으로 파이프됩니다.

worker.stdin

추가된 버전: v10.5.0

stdin: trueWorker 생성자에 전달된 경우, 이것은 쓰기 가능한 스트림입니다. 이 스트림에 쓰여진 데이터는 작업자 스레드에서 process.stdin으로 사용할 수 있게 됩니다.

worker.stdout

추가된 버전: v10.5.0

이것은 작업자 스레드 내부의 process.stdout에 쓰여진 데이터를 포함하는 읽기 가능한 스트림입니다. stdout: trueWorker 생성자에 전달되지 않은 경우, 데이터는 부모 스레드의 process.stdout 스트림으로 파이프됩니다.

worker.terminate()

[기록]

버전변경 사항
v12.5.0이 함수는 이제 Promise를 반환합니다. 콜백을 전달하는 것은 더 이상 사용되지 않으며, 작업자가 실제로 동기적으로 종료되었기 때문에 이 버전까지는 쓸모가 없었습니다. 종료는 이제 완전히 비동기적인 작업입니다.
v10.5.0추가된 버전: v10.5.0

가능한 한 빨리 작업자 스레드에서 모든 JavaScript 실행을 중지합니다. 'exit' 이벤트가 발생할 때 완료되는 종료 코드에 대한 Promise를 반환합니다.

worker.threadId

추가된 버전: v10.5.0

참조된 스레드에 대한 정수 식별자입니다. 작업자 스레드 내부에서는 require('node:worker_threads').threadId로 사용할 수 있습니다. 이 값은 단일 프로세스 내의 각 Worker 인스턴스에 대해 고유합니다.

worker.unref()

추가된 버전: v10.5.0

작업자에서 unref()를 호출하면 이벤트 시스템에서 이것이 유일한 활성 핸들인 경우 스레드가 종료될 수 있습니다. 작업자가 이미 unref()된 경우 unref()를 다시 호출해도 효과가 없습니다.

참고 사항

stdio의 동기식 차단

Worker<MessagePort>를 통해 메시지 전달을 활용하여 stdio와 상호 작용을 구현합니다. 이는 Worker에서 발생하는 stdio 출력이 Node.js 이벤트 루프를 차단하는 수신 측의 동기식 코드에 의해 차단될 수 있음을 의미합니다.

js
import { Worker, isMainThread } from 'node:worker_threads'

if (isMainThread) {
  new Worker(new URL(import.meta.url))
  for (let n = 0; n < 1e10; n++) {
    // 작업을 시뮬레이션하기 위한 루프.
  }
} else {
  // 이 출력은 메인 스레드의 for 루프에 의해 차단됩니다.
  console.log('foo')
}
js
'use strict'

const { Worker, isMainThread } = require('node:worker_threads')

if (isMainThread) {
  new Worker(__filename)
  for (let n = 0; n < 1e10; n++) {
    // 작업을 시뮬레이션하기 위한 루프.
  }
} else {
  // 이 출력은 메인 스레드의 for 루프에 의해 차단됩니다.
  console.log('foo')
}

프리로드 스크립트에서 워커 스레드 시작

프리로드 스크립트(-r 명령줄 플래그를 사용하여 로드 및 실행되는 스크립트)에서 워커 스레드를 시작할 때 주의하십시오. execArgv 옵션을 명시적으로 설정하지 않으면 새 워커 스레드는 실행 중인 프로세스의 명령줄 플래그를 자동으로 상속하고 메인 스레드와 동일한 프리로드 스크립트를 프리로드합니다. 프리로드 스크립트가 무조건 워커 스레드를 시작하면 스폰되는 모든 스레드가 애플리케이션이 충돌할 때까지 다른 스레드를 스폰합니다.