워커 스레드
소스 코드: lib/worker_threads.js
node:worker_threads
모듈은 JavaScript를 병렬로 실행하는 스레드를 사용할 수 있게 해줍니다. 접근하려면 다음과 같이 하세요.
const worker = require('node:worker_threads')
워커(스레드)는 CPU를 많이 사용하는 JavaScript 작업을 수행하는 데 유용합니다. I/O 집약적인 작업에는 그다지 도움이 되지 않습니다. Node.js에 내장된 비동기 I/O 작업이 워커보다 더 효율적입니다.
child_process
또는 cluster
와 달리 worker_threads
는 메모리를 공유할 수 있습니다. 이를 위해 ArrayBuffer
인스턴스를 전송하거나 SharedArrayBuffer
인스턴스를 공유합니다.
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
사용"을 참조하세요.
워커 스레드는 기본적으로 프로세스 특정 옵션이 아닌 옵션을 상속합니다. 워커 스레드 옵션, 특히 argv
및 execArgv
옵션을 사용자 정의하는 방법은 워커 생성자 옵션
을 참조하세요.
worker.getEnvironmentData(key)
[히스토리]
버전 | 변경 사항 |
---|---|
v17.5.0, v16.15.0 | 더 이상 실험적 기능이 아닙니다. |
v15.12.0, v14.18.0 | 추가됨: v15.12.0, v14.18.0 |
워커 스레드 내에서 worker.getEnvironmentData()
는 스폰된 스레드의 worker.setEnvironmentData()
로 전달된 데이터의 복제본을 반환합니다. 모든 새로운 Worker
는 환경 데이터의 자체 복사본을 자동으로 받습니다.
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
입니다.
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 값입니다.
객체를 전송 불가능한 것으로 표시합니다. object
가 port.postMessage()
호출의 전송 목록에 있으면 오류가 발생합니다. object
가 기본값인 경우 이는 no-op입니다.
특히, 이는 전송되는 것이 아니라 복제될 수 있고 보내는 측의 다른 객체에서 사용되는 객체에 적합합니다. 예를 들어, Node.js는 Buffer
풀에 사용하는 ArrayBuffer
를 이것으로 표시합니다.
이 작업은 취소할 수 없습니다.
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()
로 전달 불가능으로 표시된 객체인지 확인합니다.
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 값.
객체를 복제 불가능으로 표시합니다. object
가 port.postMessage()
호출에서 message
로 사용되면 오류가 발생합니다. object
가 원시 값인 경우 아무 작업도 수행되지 않습니다.
이는 ArrayBuffer
또는 Buffer
와 같은 객체에는 아무런 영향을 미치지 않습니다.
이 작업은 되돌릴 수 없습니다.
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
port
<MessagePort> 전송할 메시지 포트.contextifiedSandbox
<Object>vm.createContext()
메서드에서 반환된 contextified 객체.- 반환: <MessagePort>
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')
를 사용하여 이 스레드에서 사용할 수 있습니다.
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
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개의 중첩된 스레드를 생성하고, 마지막 스레드가 메인 스레드와 통신하려고 합니다.
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
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.0 | v12.3.0에 추가됨 |
port
<MessagePort> | <BroadcastChannel>- 반환 값: <Object> | <undefined>
주어진 MessagePort
에서 단일 메시지를 수신합니다. 사용 가능한 메시지가 없으면 undefined
가 반환되고, 그렇지 않으면 MessagePort
의 큐에서 가장 오래된 메시지에 해당하는 메시지 페이로드를 포함하는 단일 message
속성을 가진 객체가 반환됩니다.
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
옵션으로 전달할 수 있는 특수 값입니다.
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 값입니다.value
가undefined
로 전달되면key
에 대해 이전에 설정된 값이 삭제됩니다.
worker.setEnvironmentData()
API는 현재 스레드와 현재 컨텍스트에서 생성된 모든 새 Worker
인스턴스에서 worker.getEnvironmentData()
의 내용을 설정합니다.
worker.threadId
추가됨: v10.5.0
현재 스레드의 정수 식별자입니다. 해당 워커 객체(있는 경우)에서는 worker.threadId
로 사용할 수 있습니다. 이 값은 단일 프로세스 내의 각 Worker
인스턴스에 대해 고유합니다.
worker.workerData
추가된 버전: v10.5.0
이 스레드의 Worker
생성자에 전달된 데이터의 클론을 포함하는 임의의 JavaScript 값입니다.
데이터는 HTML 구조적 클론 알고리즘에 따라 postMessage()
를 사용하는 것처럼 클론됩니다.
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
인스턴스와 비동기 일대다 통신을 할 수 있도록 합니다.
'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
인스턴스를 참조하는 port1
및 port2
속성을 가진 객체를 생성합니다.
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 |
- 확장: <EventTarget>
worker.MessagePort
클래스의 인스턴스는 비동기 양방향 통신 채널의 한쪽 끝을 나타냅니다. 서로 다른 Worker
간에 구조화된 데이터, 메모리 영역 및 기타 MessagePort
를 전송하는 데 사용할 수 있습니다.
이 구현은 브라우저 MessagePort
와 일치합니다.
Event: 'close'
추가된 버전: v10.5.0
채널의 한쪽이 연결 해제되면 한 번 'close'
이벤트가 발생합니다.
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
error
<Error> Error 객체
메시지 역직렬화에 실패하면 '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 |
value
<any>transferList
<Object[]>
이 채널의 수신 측에 JavaScript 값을 보냅니다. value
는 HTML 구조적 복제 알고리즘과 호환되는 방식으로 전송됩니다.
특히, JSON
과의 중요한 차이점은 다음과 같습니다.
value
에는 순환 참조가 포함될 수 있습니다.value
에는RegExp
,BigInt
,Map
,Set
등과 같은 기본 제공 JS 유형의 인스턴스가 포함될 수 있습니다.value
에는ArrayBuffer
와SharedArrayBuffer
를 모두 사용하는 형식화된 배열이 포함될 수 있습니다.value
에는WebAssembly.Module
인스턴스가 포함될 수 있습니다.value
에는 다음을 제외한 네이티브(C++ 지원) 객체가 포함될 수 없습니다.
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)
transferList
는 ArrayBuffer
, MessagePort
및 FileHandle
객체 목록일 수 있습니다. 전송 후에는 ( value
에 포함되어 있지 않더라도) 채널의 발신 측에서 더 이상 사용할 수 없습니다. 하위 프로세스와 달리 현재 네트워크 소켓과 같은 핸들 전송은 지원되지 않습니다.
value
에 SharedArrayBuffer
인스턴스가 포함된 경우 해당 인스턴스는 어느 스레드에서든 액세스할 수 있습니다. transferList
에 나열할 수 없습니다.
value
에는 transferList
에 없는 ArrayBuffer
인스턴스가 여전히 포함될 수 있습니다. 이 경우 기본 메모리는 이동되지 않고 복사됩니다.
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 및 버퍼 전송 시 고려 사항
모든 TypedArray
및 Buffer
인스턴스는 기본 ArrayBuffer
에 대한 뷰입니다. 즉, 원시 데이터를 실제로 저장하는 것은 ArrayBuffer
이며, TypedArray
및 Buffer
객체는 데이터를 보고 조작하는 방법을 제공합니다. 동일한 ArrayBuffer
인스턴스에 대해 여러 개의 뷰를 만드는 것이 가능하며 일반적입니다. 전송 목록을 사용하여 ArrayBuffer
를 전송할 때 매우 주의해야 합니다. 이렇게 하면 동일한 ArrayBuffer
를 공유하는 모든 TypedArray
및 Buffer
인스턴스를 사용할 수 없게 됩니다.
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 객체로 복제됩니다.
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
객체와 같은 많은 내장 객체로 확장됩니다.
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
- 반환 값: <boolean>
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
- 확장: <EventEmitter>
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_ENV
가Worker
생성자에env
옵션으로 전달되지 않은 경우). Windows에서는 메인 스레드와 달리 환경 변수 복사본이 대소문자를 구분하여 작동합니다.process.title
은 수정할 수 없습니다.- 신호는
process.on('...')
를 통해 전달되지 않습니다. - 실행은
worker.terminate()
가 호출된 결과로 언제든지 중지될 수 있습니다. - 부모 프로세스의 IPC 채널에 접근할 수 없습니다.
trace_events
모듈은 지원되지 않습니다.- 네이티브 애드온은 특정 조건을 충족하는 경우에만 여러 스레드에서 로드할 수 있습니다.
다른 Worker
내에서 Worker
인스턴스를 생성할 수 있습니다.
웹 워커 및 node:cluster
모듈과 마찬가지로 스레드 간 메시지 전달을 통해 양방향 통신을 달성할 수 있습니다. 내부적으로 Worker
는 Worker
가 생성될 때 이미 서로 연결된 내장 MessagePort
쌍을 가지고 있습니다. 부모 측의 MessagePort
객체는 직접 노출되지는 않지만, 해당 기능은 부모 스레드의 Worker
객체에서 worker.postMessage()
및 worker.on('message')
이벤트를 통해 노출됩니다.
사용자 정의 메시징 채널(관심사의 분리를 용이하게 하므로 기본 글로벌 채널을 사용하는 것보다 권장됨)을 생성하기 위해 사용자는 어느 스레드에서든 MessageChannel
객체를 생성하고 기존 채널(예: 글로벌 채널)을 통해 해당 MessageChannel
의 MessagePort
중 하나를 다른 스레드로 전달할 수 있습니다.
메시지가 전달되는 방법과 스레드 경계를 통해 성공적으로 전송할 수 있는 JavaScript 값 유형에 대한 자세한 내용은 port.postMessage()
를 참조하십시오.
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.0 | filename 매개변수는 data: 프로토콜을 사용하는 WHATWG URL 객체일 수 있습니다. |
v14.9.0 | trackUnmanagedFds 옵션이 기본적으로 true 로 설정되었습니다. |
v14.6.0, v12.19.0 | trackUnmanagedFds 옵션이 도입되었습니다. |
v13.13.0, v12.17.0 | transferList 옵션이 도입되었습니다. |
v13.12.0, v12.17.0 | filename 매개변수는 file: 프로토콜을 사용하는 WHATWG URL 객체일 수 있습니다. |
v13.4.0, v12.16.0 | argv 옵션이 도입되었습니다. |
v13.2.0, v12.16.0 | resourceLimits 옵션이 도입되었습니다. |
v10.5.0 | v10.5.0에서 추가됨 |
filename
<string> | <URL> 워커의 메인 스크립트 또는 모듈의 경로입니다. 절대 경로이거나./
또는../
로 시작하는 현재 작업 디렉토리(current working directory)를 기준으로 하는 상대 경로여야 합니다. 또는file:
또는data:
프로토콜을 사용하는 WHATWGURL
객체여야 합니다.data: URL
을 사용하는 경우, 데이터는 ECMAScript 모듈 로더를 사용하여 MIME 유형에 따라 해석됩니다.options.eval
이true
이면, 이 값은 경로가 아닌 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
err
<Error>
워커 스레드에서 처리되지 않은 예외가 발생하면 'error'
이벤트가 발생합니다. 이 경우 워커는 종료됩니다.
이벤트: 'exit'
추가된 버전: v10.5.0
exitCode
<integer>
워커가 중지되면 '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
error
<Error> 에러 객체
메시지 역직렬화에 실패하면 '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 |
Worker의 현재 상태의 V8 스냅샷에 대한 읽기 가능한 스트림을 반환합니다. 자세한 내용은 v8.getHeapSnapshot()
를 참조하십시오.
'exit'
이벤트가 발생하기 전에 발생할 수 있는 워커 스레드가 더 이상 실행 중이지 않으면 반환된 Promise
는 ERR_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
시간을 전혀 축적하지 않지만 여전히 메시지를 처리할 수 있는 방법을 보여줍니다.
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
value
<any>transferList
<Object[]>
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: true
가 Worker
생성자에 전달되지 않은 경우, 데이터는 부모 스레드의 process.stderr
스트림으로 파이프됩니다.
worker.stdin
추가된 버전: v10.5.0
stdin: true
가 Worker
생성자에 전달된 경우, 이것은 쓰기 가능한 스트림입니다. 이 스트림에 쓰여진 데이터는 작업자 스레드에서 process.stdin
으로 사용할 수 있게 됩니다.
worker.stdout
추가된 버전: v10.5.0
이것은 작업자 스레드 내부의 process.stdout
에 쓰여진 데이터를 포함하는 읽기 가능한 스트림입니다. stdout: true
가 Worker
생성자에 전달되지 않은 경우, 데이터는 부모 스레드의 process.stdout
스트림으로 파이프됩니다.
worker.terminate()
[기록]
버전 | 변경 사항 |
---|---|
v12.5.0 | 이 함수는 이제 Promise를 반환합니다. 콜백을 전달하는 것은 더 이상 사용되지 않으며, 작업자가 실제로 동기적으로 종료되었기 때문에 이 버전까지는 쓸모가 없었습니다. 종료는 이제 완전히 비동기적인 작업입니다. |
v10.5.0 | 추가된 버전: v10.5.0 |
- 반환 값: <Promise>
가능한 한 빨리 작업자 스레드에서 모든 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 이벤트 루프를 차단하는 수신 측의 동기식 코드에 의해 차단될 수 있음을 의미합니다.
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')
}
'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
옵션을 명시적으로 설정하지 않으면 새 워커 스레드는 실행 중인 프로세스의 명령줄 플래그를 자동으로 상속하고 메인 스레드와 동일한 프리로드 스크립트를 프리로드합니다. 프리로드 스크립트가 무조건 워커 스레드를 시작하면 스폰되는 모든 스레드가 애플리케이션이 충돌할 때까지 다른 스레드를 스폰합니다.