Node.js

Worker_threads (멀티 스레드)

쿠와와 2020. 12. 10. 17:35

노드에서 멀티 스레드 방식으로 작업할 수 있음.

isMainThread: 현재 코드가 메인 스레드에서 실행되는지, 워커 스레드에서 실행되는지 구분

메인 스레드에서는 new Worker를 통해 현재 파일(__filename)을 워커 스레드에서 실행시킴

worker.postMessage로 부모에서 워커로 데이터를 보냄

parentPort.on(‘message’)로 부모로부터 데이터를 받고, postMessage로 데이터를 보냄

 

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads')

if(isMainThread){//메인스레드
    const threads = new Set();  // 중복되지 않는 배열
    threads.add(new Worker(__filename, {
        workerData: {start : 1},
    }));
    threads.add(new Worker(__filename, {
        workerData: {start : 2},
    }));
    // const worker = new Worker(__filename);
    for (let worker of threads){
        worker.on('message', (value) => console.log('워커로부터', value));
        worker.on('exit', () => {
            threads.delete(worker);
            if( threads.size == 0){
                console.log('워커 끝~');
            }
        });
        // worker.postMessage('ping')
    }
} else {//워커스레드
    // 우리가 직접 결과물 리턴까지 해줘야함 
    // 이점은 코드가 동시에 돌아간다.
    // parentPort.on('message', (value) => {
    //     console.log('부모로부터', value);
    //     parentPort.postMessage('pong');
    //     parentPort.close();
    // })
    const data = workerData;    // 데이터를 받아옴
    parentPort.postMessage(data.start + 100);       //
}

 

예를 들어 소수를 찾는 함수를 작성해보자. 결과는 ??

const min = 2;
const max = 10_000_000;
const primes = [];

// 소수 찾기 
function generatePrimes(start, range){
    let isPrime = true;
    const end = start + range;
    for (let i = start; i < end; i++){
        for (let j = min; j < Math.sqrt(end); j++){
            if(i !== j && i % j == 0){
                isPrime = false;
                break;
            }
        }
        if (isPrime){
            primes.push(i);
        }
        isPrime = true;
    }
}

console.time('prime');
generatePrimes(min, max);
console.timeEnd('prime');
console.log(primes.length);

결과는 ??

prime: 11.902s 

64579

이렇게 나온다면 12초 동안은 아무것도 할 수 없는 것이다. 

멀티쓰레드를 이용해보자

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

const min = 2;
let primes = [];

// 소수 찾기 
function findPrimes(start, range){
    let isPrime = true;
    let end = start + range;
    for (let i = start; i < end; i++){
        for (let j = min; j < Math.sqrt(end); j++){
            if(i !== j && i % j == 0){
                isPrime = false;
                break;
            }
        }
        if (isPrime){
            primes.push(i);
        }
        isPrime = true;
    }
}

if (isMainThread){
    const max = 10000000;
    const threadCount = 8;
    const threads = new Set();
    const range = Math.ceil((max - min) / threadCount);
    let start = min;
    console.time('prime');
    for (let i = 0; i < threadCount - 1; i++){
        const wStart = start;
        threads.add(new Worker(__filename, { workerData: {start: wStart, range}}));
        start += range;
    }
    threads.add(new Worker(__filename, { workerData: {start, range: range + ((max - min + 1) % threadCount) }}));
    for(let worker of threads){
        worker.on('error', (err) => {
            throw err;
        });
        worker.on('exit', () => {
            threads.delete(worker);
            if(threads.size == 0){
                console.timeEnd('prime');
                console.log(primes.length);
            }
        });
        worker.on('message', (msg) => {
            primes = primes.concat(msg);
        });
    }
}else {
    findPrimes(workerData.start, workerData.range);
    parentPort.postMessage(primes);
}

결과는 ??

prime: 2.248s
664579

엄청나게 빨라진 것을 볼 수 있다.

자기 컴퓨터에 맞는 thread_count를 찾아서 적용해보자.