javascript의 공유 메모리(shared memory)에 대해 알아보고, SharedArrayBuffer와 TypedArray에 대해 알아봅니다.
브라우저에는 세 종류의 멀티쓰레딩 방법이 있습니다.
- web worker
- shared werker
- service worker
node.js에는 worker_thread 모듈이 존재합니다.
각자 용도가 있지만, 대부분 이벤트 루프 기반 메세지 패싱 시스템 API 기반으로 동작합니다.
이는 여러 스레드가 동시에 공유 메모리에 접근할 수 있는 병럴 처리 방식보다 효율이 떨어지게 됩니다.
자바스크립트는 멀티쓰레딩의 힘을 더욱 강력하게 해주는 두 가지 도구를 갖고 있습니다.
- Atomics 객체
- SharedArrayBuffer 클래스
공유 메모리 사용 예제 : browser
공유 메모리는 약간의 조정이 필요하지만 node.js, 브라우저 환경 양쪽에서 유사하게 활용할 수 있습니다.
간한한 예제로 알아봅시다.
예제는 아래 github 레포지토리의 소스코드를 사용합니다.
https://github.com/MultithreadedJSBook/code-samples
GitHub - MultithreadedJSBook/code-samples: Code samples for the book Multithreaded JavaScript, O'Reilly, 2021
Code samples for the book Multithreaded JavaScript, O'Reilly, 2021 - GitHub - MultithreadedJSBook/code-samples: Code samples for the book Multithreaded JavaScript, O'Reilly, 2021
github.com
Example 4-1. ch4-web-workers/index.html
<html>
<head>
<title>Shared Memory Hello World</title>
<script src="main.js"></script>
</head>
</html>
Example 4-2. ch4-web-workers/main.js
if (!crossOriginIsolated) { // 1
throw new Error('Cannot use SharedArrayBuffer');
}
const worker = new Worker('worker.js');
const buffer = new SharedArrayBuffer(1024); // 2
const view = new Uint8Array(buffer); // 3
console.log('now', view[0]);
worker.postMessage(buffer);
setTimeout(() => {
console.log('later', view[0]);
console.log('prop', buffer.foo); // 4
}, 500);
- crossOriginIsolated가 true이면 SharedArrayBuffer를 사용할 수 있습니다.
- 1kb 버퍼를 초기화 합니다
- 버퍼에 대한 view가 생성됩니다.
- 버퍼에서 수정된 foo 속성의 값을 읽습니다. (뭔가 이상하다 생각할 수 있지만, 뒤에 설명합니다.)
- 이 값은 현재 실행 중인 JavaScript 코드가 SharedArrayBuffer 인스턴스를 인스턴스화할 수 있는지 여부를 알려줍니다.
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
SharedArrayBuffer 인스턴스를 사용할 경우, 이러한 헤더를 설정해야 한다는 것을 기억해야 합니다.
web worker가 인스턴스화된 후 SharedArrayBuffer의 인스턴스도 인스턴스화됩니다.
인수(이 경우 1,024)는 버퍼에 할당된 바이트 수입니다.
이 버퍼는 생성된 후에 크기가 줄어들거나 커질 수 없습니다.
(몇몇 환경은 가능 : https://github.com/tc39/proposal-resizablearraybuffer)
view에 대해서는 이후에 좀 더 설명합니다. 지금은 버퍼에 읽고 쓰는 수단이라 생각하시면 됩니다.
해당 view를 배열 인덱스 구문을 사용하여 읽을 수 있습니다.
view[0]에 대한 호출을 로깅하여 버퍼의 0번째 바이트를 검사할 수 있습니다.
Example 4-3. ch4-web-workers/worker.js
self.onmessage = ({data: buffer}) => {
buffer.foo = 42; // 1
const view = new Uint8Array(buffer);
view[0] = 2; // 2
console.log('updated in worker');
};
- 버퍼 객체의 속성을 작성합니다.
- 0번째 인덱스는 숫자 2로 설정됩니다.
now 0 | main.js:10:9 |
updated in worker | worker.js:5:11 |
later 2 | main.js:15:11 |
prop undefined | main.js:16:11 |
초기 postmessage 시점 외에는 스레드 간 메세지 전달은 발생하지 않습니다.
main은 버퍼를 메세지로 전달하고, worker는 버퍼에 값을 쓰며, main은 0.5초 후에 worker에 의해 쓰여진 값 2를 출력합니다.
보통 이러한 방식으로 멀티쓰레드 프로그램을 작성하지는 않으며, 매우 단순한 예제입니다.
공유 메모리 사용 예제 : node.js
#!/usr/bin/env node
const { Worker } = require('worker_threads');
const worker = new Worker(__dirname + '/worker-node.js');
const buffer = new SharedArrayBuffer(1024);
const view = new Uint8Array(buffer);
console.log('now', view[0]);
worker.postMessage(buffer);
setTimeout(() => {
console.log('later', view[0]);
console.log('prop', buffer.foo);
worker.unref();
}, 500);
- Worker 전역 객체를 사용할 수 없기 때문에 worker_threads 모듈에서 Worker 속성을 가져와서 액세스합니다.
- 작업자 스레드 객체를 인스턴스화할 때, 브라우저에서 허용하는 것보다 더 명확한 경로를 제공해야 합니다.
- 브라우저는 worker.js만으로도 충분하지만 ./worker-node.js 경로가 필요합니다.
- worker.unref() 호출이 추가되어 작업자가 프로세스를 계속 실행하지 못하게 합니다.
Example 4-5. ch4-web-workers/worker-node.js
const { parentPort } = require('worker_threads');
parentPort.on('message', (buffer) => {
buffer.foo = 42;
const view = new Uint8Array(buffer);
view[0] = 2;
console.log('updated in worker');
});
$ node main-node.js
SharedArrayBuffer and TypedArrays
const ab = new ArrayBuffer(8);
const view = new Uint8Array(ab)
for (i = 0; i < 8; i++) view[i] = i;
console.log(view);
// Uint8Array(8) [
// 0, 1, 2, 3,
// 4, 5, 6, 7
// ]
ab.byteLength; // 8
ab.slice(); // 0, 1, 2, 3, 4, 5, 6, 7
ab.slice(4, 6); // 4, 5
ab.slice(-3, -2); // 5

아래는 TypedArray에서 확장되는 View 클래스 목록입니다.
Class | Bytes | Minimum Value | Maximum Value |
Int8Array | 1 | –128 | 127 |
Uint8Array | 1 | 0 | 255 |
Uint8ClampedArray | 1 | 0 | 255 |
Int16Array | 2 | –32,768 | 32,767 |
Uint16Array | 2 | 0 | 65,535 |
Int32Array | 4 | –2,147,483,648 | 2,147,483,647 |
Uint32Array | 4 | 0 | 4294967295 |
Float32Array | 4 | 1.4012984643e-45 | 3.4028235e38 |
Float64Array | 8 | 5e–324 | 1.7976931348623157e308 |
BigInt64Array | 8 | –9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
BigUint64Array | 8 | 0 | 18,446,744,073,709,551,615 |
- 클래스 열은 인스턴스화에 사용할 수 있는 클래스의 이름입니다.
- 이러한 클래스는 전역 클래스이며 최신 JavaScript 엔진에서 액세스할 수 있습니다.
- Bytes 열은 뷰의 각 개별 요소를 나타내는 데 사용되는 바이트 수입니다.
- 최소값 및 최대값 열에는 버퍼의 요소를 나타내는 데 사용할 수 있는 유효한 숫자 범위가 표시됩니다.
const buffer = new ArrayBuffer(16); // 16 bytes
const view64 = new Float64Array(buffer);
view64[0] = 1.1234567890123456789; // bytes 0 - 7
console.log(view64[0]); // 1.1234567890123457
const view32 = new Float32Array(buffer);
view32[2] = 1.1234567890123456789; // bytes 8 - 11
console.log(view32[2]); // 1.1234568357467651
이 경우 float64 숫자의 소수점 정밀도는 소수점 이하 15번째까지 정확하지만

- 먼저 Math.trunc()에 전달된 것처럼 숫자를 정수로 변환해야 합니다.
- 값이 허용 가능한 범위를 벗어나면 계수(%) 연산자를 사용하는 것처럼 순환되어 0에서 재설정됩니다.
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);
view[0] = 255; view[1] = 256;
view[2] = 257; view[3] = -1;
view[4] = 1.1; view[5] = 1.999;
view[6] = -1.1; view[7] = -1.9;
console.log(view);
아래 출력은 다음과 같습니다.
{
"0": 255,
"1": 0,
"2": 1,
"3": 255,
"4": 1,
"5": 1,
"6": 255,
"7": 255
}
- 음수 값이 쓰여지면 0으로 변환됩니다.
- 255보다 큰 값이 쓰여지면 255로 변환됩니다.
- 정수가 아닌 값이 제공되면 대신 Math.round()에 전달됩니다.
참고:
https://www.oreilly.com/library/view/multithreaded-javascript/9781098104429/
Multithreaded JavaScript
Traditionally, JavaScript has been a single-threaded language. Nearly all online forum posts, books, online documentation, and libraries refer to the language as single threaded. Thanks to recent advancements in the … - Selection from Multithreaded JavaS
www.oreilly.com
'FrontEnd' 카테고리의 다른 글
멀티쓰레드 Javascript 3편 : 상호 배제를 위한 조정(coordination) (0) | 2023.01.24 |
---|---|
멀티쓰레드 Javascript 2편 : Atomics 객체와 원자성, 직렬화 (0) | 2023.01.24 |
자바스크립트 스킬 티어 리스트 (0) | 2023.01.22 |
Rollup.js 알아보기 1편 : Rollup.js 튜토리얼 (0) | 2023.01.21 |
Content security policy[콘텐츠 보안 정책]에 대해 알아보기 (0) | 2023.01.21 |