자바 스크립트와 스레드


137

JavaScript에서 멀티 스레딩을 수행 할 수있는 방법이 있습니까?


2
JavaScript에는 스레드가 적어도 현재 형식으로 포함되어 있지 않습니다. 정확히 무엇을하려고합니까?
Eric Pohl

3
수락 된 답변을 이것으로 변경할 수 있습니까? stackoverflow.com/a/30891727/2576706 향후 사용자를 위해 훨씬 더 개발되었습니다.
Ludovic Feltz

답변:


109

최신 지원 정보는 http://caniuse.com/#search=worker 를 참조 하십시오 .

다음은 2009 년 경 지원 상태입니다.


Google에 제공하려는 단어는 JavaScript Worker Threads입니다.

Gears 외에도 현재 사용할 수있는 것이 없지만이를 구현하는 방법에 대한 많은 이야기가 있으므로 미래의 대답이 의심 할 여지 없이이 질문을 보았습니다.

Gears 관련 문서는 다음과 같습니다. WorkerPool API

WHATWG에는 작업자 스레드에 대한 초안 권장 사항이 있습니다. 웹 작업자

그리고 Mozilla의 DOM Worker Threads도 있습니다.


업데이트 : 2009 년 6 월, JavaScript 스레드에 대한 브라우저 지원 상태

Firefox 3.5 에는 웹 워커가 있습니다. 웹 워커의 데모를보고 싶다면 :

Firefox에 Gears 플러그인을 설치할 수도 있습니다.

Safari 4WebKit 야간 에는 작업자 스레드가 있습니다.

Chrome 에는 Gears가 내장되어 있으므로 사용자의 확인 메시지가 필요하지만 스레드를 수행 할 수 있습니다 (Gears 플러그인이 설치된 모든 브라우저에서 작동하지만 웹 작업자에게 다른 API를 사용함).

  • Google Gears WorkerPool 데모 (IE는 상호 작용을 차단할 정도로 느리게 실행되지만 Chrome 및 Firefox에서 테스트하기에는 너무 빨리 실행되므로 좋은 예가 아님)

IE8IE9 는 Gears 플러그인이 설치된 스레드 만 수행 할 수 있습니다.


1
: 사파리 4가 지원하는 웹 노동자 있지만 만 파이어 폭스가 지원하는 PostMessage를 통해 복잡한 객체를 전달 나타납니다 hacks.mozilla.org/2009/07/working-smarter-not-harder Bespin 프로젝트에서 실제 사용에 해당 게시물의 마지막 단락을 참조하십시오 Google Gears 측면에서 Worker API를 구현하고 Safari 4의 Worker 구현에 누락 된 기능을 추가 하고 postMessage 인터페이스 위에 투명한 사용자 지정 이벤트를 구현 한 방법에 대한 세부 정보 를 추가 하는 shim에 대한 링크가 있습니다.
Sam Hasler

6
이제 IE9가 종료되었습니다. "IE8은 Gears 플러그인이 설치된 스레드 만 수행 할 수 있습니다"를 "IE8 및 IE9는 Gears 플러그인이 설치된 스레드 만 수행 할 수 있습니다"로 업데이트 할 수 있습니다
BenoitParis

2
@ inf3rno 다른 스레드에서 긴 계산을 수행하여 브라우저 UI 속도를 늦추지 않습니다.
Sam Hasler

6
@SamHasler 답을 수정하고 싶을 수도 있습니다. 웹 워커는 이제 모든 최신 데스크탑 브라우저에서 지원됩니다. 또한 caniuse.com/#search=worker
Rob W

2
@SamHasler 또한 Google Gears가 더 이상 지원되지 않습니다.
skeggse

73

JavaScript에서 멀티 스레딩 및 비동기를 수행하는 다른 방법

HTML5 JavaScript 이전에는 페이지 당 하나의 스레드 만 실행할 수있었습니다.

와 비동기 실행 시뮬레이션 일부 해키 방법 있었다 수율은 , setTimeout(), setInterval(), XMLHttpRequest또는 이벤트 핸들러 (예와 본 게시물 마지막 참조 수율setTimeout()).

그러나 HTML5에서는 이제 Worker Threads를 사용하여 함수 실행을 병렬화 할 수 있습니다. 다음은 사용 예입니다.


실제 멀티 스레딩

멀티 스레딩 : JavaScript 작업자 스레드

HTML5 에는 Web Worker Threads가 도입되었습니다 ( 브라우저 호환성 참조 ).
참고 : IE9 및 이전 버전은이를 지원하지 않습니다.

이 작업자 스레드는 페이지 성능에 영향을주지 않으면 서 백그라운드에서 실행되는 JavaScript 스레드입니다. Web Worker 에 대한 자세한 내용은 설명서 또는 이 자습서를 참조하십시오 .

다음은 MAX_VALUE로 계산되고 현재 계산 된 값을 페이지에 표시하는 3 개의 Web Worker 스레드가있는 간단한 예입니다.

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo){ return window.URL.createObjectURL(new Blob([foo.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]],{type:'text/javascript'})); }

var MAX_VALUE = 10000;

/*
 *	Here are the workers
 */
//Worker 1
var worker1 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
//We add a listener to the worker to get the response and show it in the page
worker1.addEventListener('message', function(e) {
  document.getElementById("result1").innerHTML = e.data;
}, false);


//Worker 2
var worker2 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker2.addEventListener('message', function(e) {
  document.getElementById("result2").innerHTML = e.data;
}, false);


//Worker 3
var worker3 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker3.addEventListener('message', function(e) {
    document.getElementById("result3").innerHTML = e.data;
}, false);


// Start and send data to our worker.
worker1.postMessage(MAX_VALUE); 
worker2.postMessage(MAX_VALUE); 
worker3.postMessage(MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

세 개의 스레드가 동시성으로 실행되고 현재 값을 페이지에 인쇄하는 것을 볼 수 있습니다. 그들은 분리 된 스레드로 백그라운드에서 실행되기 때문에 페이지를 고정시키지 않습니다.


멀티 스레딩 : 여러 iframe 사용

이를 달성하는 또 다른 방법은 여러 개의 iframe 을 사용하는 것입니다. 각 iframe 은 스레드를 실행합니다. 우리는 줄 수있는 iframe이 URL과에 의해 일부 매개 변수 iframe이이 결과를 얻고 (뒷면을 인쇄하기 위해 자신의 부모와 통신 할 수 iframe이이 같은 도메인에 있어야합니다).

이 예제는 모든 브라우저에서 작동하지 않습니다! iframe은 일반적으로 기본 페이지와 동일한 스레드 / 프로세스로 실행되지만 Firefox와 Chromium은 다르게 처리하는 것으로 보입니다.

코드 스 니펫은 여러 HTML 파일을 지원하지 않으므로 여기에 다른 코드를 제공합니다.

index.html :

//The 3 iframes containing the code (take the thread id in param)
<iframe id="threadFrame1" src="thread.html?id=1"></iframe>
<iframe id="threadFrame2" src="thread.html?id=2"></iframe>
<iframe id="threadFrame3" src="thread.html?id=3"></iframe>

//Divs that shows the result
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>


<script>
    //This function is called by each iframe
    function threadResult(threadId, result) {
        document.getElementById("result" + threadId).innerHTML = result;
    }
</script>

thread.html :

//Get the parameters in the URL: http://stackoverflow.com/a/1099670/2576706
function getQueryParams(paramName) {
    var qs = document.location.search.split('+').join(' ');
    var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;
    while (tokens = re.exec(qs)) {
        params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
    }
    return params[paramName];
}

//The thread code (get the id from the URL, we can pass other parameters as needed)
var MAX_VALUE = 100000;
(function thread() {
    var threadId = getQueryParams('id');
    for(var i=0; i<MAX_VALUE; i++){
        parent.threadResult(threadId, i);
    }
})();

멀티 스레딩 시뮬레이션

단일 스레드 : setTimeout ()을 사용하여 JavaScript 동시성을 에뮬레이션

'순진한'방법은 다음 setTimeout()과 같이 함수를 차례로 실행하는 것입니다 .

setTimeout(function(){ /* Some tasks */ }, 0);
setTimeout(function(){ /* Some tasks */ }, 0);
[...]

그러나이 방법 각 작업이 하나씩 실행되므로 작동하지 않습니다 .

다음과 같이 함수를 재귀 적으로 호출하여 비동기 실행을 시뮬레이션 할 수 있습니다.

var MAX_VALUE = 10000;

function thread1(value, maxValue){
    var me = this;
    document.getElementById("result1").innerHTML = value;
    value++;
  
    //Continue execution
    if(value<=maxValue)
        setTimeout(function () { me.thread1(value, maxValue); }, 0);
}

function thread2(value, maxValue){
    var me = this;
    document.getElementById("result2").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread2(value, maxValue); }, 0);
}

function thread3(value, maxValue){
    var me = this;
    document.getElementById("result3").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread3(value, maxValue); }, 0);
}

thread1(0, MAX_VALUE);
thread2(0, MAX_VALUE);
thread3(0, MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

보시다시피이 두 번째 방법은 매우 느리고 메인 스레드를 사용하여 기능을 실행하기 때문에 브라우저를 정지시킵니다.


단일 스레드 : 수율로 JavaScript 동시성 에뮬레이션

Yield ECMAScript 6 의 새로운 기능으로 Firefox 및 Chrome의 가장 오래된 버전에서만 작동합니다 (Chrome에서는 chrome : // flags / # enable-javascript-harmony에 나타나는 Experimental JavaScript 를 활성화해야 함).

yield 키워드는 생성기 함수 실행을 일시 중지시키고 yield 키워드 다음에 나오는 표현식의 값은 생성자의 호출자에게 반환됩니다. return 키워드의 생성기 기반 버전으로 생각할 수 있습니다.

생성기를 사용하면 함수 실행을 일시 중단하고 나중에 다시 시작할 수 있습니다. 발전기는 trampolining 이라는 기술로 기능을 예약하는 데 사용할 수 있습니다 .

예를 들면 다음과 같습니다.

var MAX_VALUE = 10000;

Scheduler = {
	_tasks: [],
	add: function(func){
		this._tasks.push(func);
	},	
	start: function(){
		var tasks = this._tasks;
		var length = tasks.length;
		while(length>0){
			for(var i=0; i<length; i++){
				var res = tasks[i].next();
				if(res.done){
					tasks.splice(i, 1);
					length--;
					i--;
				}
			}
		}
	}	
}


function* updateUI(threadID, maxValue) {
  var value = 0;
  while(value<=maxValue){
	yield document.getElementById("result" + threadID).innerHTML = value;
	value++;
  }
}

Scheduler.add(updateUI(1, MAX_VALUE));
Scheduler.add(updateUI(2, MAX_VALUE));
Scheduler.add(updateUI(3, MAX_VALUE));

Scheduler.start()
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>


3
이것이 가장 좋은 대답이어야합니다.
Jerry Liu

14

HTML5 "side-specs"를 사용하면 setTimeout (), setInterval () 등으로 더 이상 자바 스크립트를 해킹 할 필요가 없습니다 .

HTML5 & Friends는 자바 스크립트 웹 워커 사양을 소개합니다 . 스크립트를 비동기 적으로 독립적으로 실행하기위한 API입니다.

사양튜토리얼에 링크합니다 .


11

JavaScript에는 진정한 스레딩이 없습니다. JavaScript는 가단 언어이므로 일부를 에뮬레이트 할 수 있습니다. 여기입니다 예를 들어 내가 일전에 우연히 만났다는.


1
"진정한 스레딩"은 무엇을 의미합니까? 녹색 스레드는 실제 스레드입니다.
Wes

10

Javascript에는 진정한 멀티 스레딩이 없지만 setTimeout()비동기 AJAX 요청을 사용하여 비동기 동작을 얻을 수 있습니다 .

정확히 무엇을 달성하려고합니까?


7

다음은 자바 스크립트에서 멀티 스레딩 을 시뮬레이션 하는 방법입니다.

이제 3 개의 스레드를 만들어 숫자 추가를 계산하고 숫자를 13으로 나누고 숫자를 3에서 10000000000까지 나눌 수 있습니다.이 세 함수는 동시성이 의미하는 것과 동시에 실행할 수 없습니다. 그러나이 함수를 동시에 재귀 적으로 실행시키는 트릭을 보여 드리겠습니다 : jsFiddle

이 코드는 내 소유입니다.

신체 부위

    <div class="div1">
    <input type="button" value="start/stop" onclick="_thread1.control ? _thread1.stop() : _thread1.start();" /><span>Counting summation of numbers till 10000000000</span> = <span id="1">0</span>
</div>
<div class="div2">
    <input type="button" value="start/stop" onclick="_thread2.control ? _thread2.stop() : _thread2.start();" /><span>Counting numbers can be divided with 13 till 10000000000</span> = <span id="2">0</span>
</div>
<div class="div3">
    <input type="button" value="start/stop" onclick="_thread3.control ? _thread3.stop() : _thread3.start();" /><span>Counting numbers can be divided with 3 till 10000000000</span> = <span id="3">0</span>
</div>

자바 스크립트 부분

var _thread1 = {//This is my thread as object
    control: false,//this is my control that will be used for start stop
    value: 0, //stores my result
    current: 0, //stores current number
    func: function () {   //this is my func that will run
        if (this.control) {      // checking for control to run
            if (this.current < 10000000000) {
                this.value += this.current;   
                document.getElementById("1").innerHTML = this.value;
                this.current++;
            }
        }
        setTimeout(function () {  // And here is the trick! setTimeout is a king that will help us simulate threading in javascript
            _thread1.func();    //You cannot use this.func() just try to call with your object name
        }, 0);
    },
    start: function () {
        this.control = true;   //start function
    },
    stop: function () {
        this.control = false;    //stop function
    },
    init: function () {
        setTimeout(function () {
            _thread1.func();    // the first call of our thread
        }, 0)
    }
};
var _thread2 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 13 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("2").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread2.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread2.func();
        }, 0)
    }
};
var _thread3 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 3 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("3").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread3.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread3.func();
        }, 0)
    }
};

_thread1.init();
_thread2.init();
_thread3.init();

이 방법이 도움이 되길 바랍니다.


6

코드를 상태 머신으로 변환하여 효과적으로 스레딩을 에뮬레이트하는 컴파일러 인 Narrative JavaScript를 사용할 수 있습니다 . 단일 선형 코드 블록에 비동기 코드를 작성할 수있는 언어에 "yielding"연산자 ( '->'로 표기)를 추가하면됩니다.


4

오늘 나올 새로운 v8 엔진이 그것을 지원합니다 (생각합니다)


3

원시 Javascript에서 할 수있는 최선의 방법은 몇 개의 비동기 호출 (xmlhttprequest)을 사용하는 것이지만 실제로는 스레딩이 아니며 매우 제한적이지 않습니다. Google Gears 는 브라우저에 여러 API를 추가하며 그 중 일부는 스레딩 지원에 사용될 수 있습니다.


1
Google Gears API는 더 이상 사용할 수 없습니다.
Ludovic Feltz

3

AJAX를 사용할 수 없거나 사용하지 않으려면 iframe 또는 10을 사용하십시오! ;) 닷넷 AJAX 등의 브라우저 간 비교 가능한 문제 나 구문 문제에 대해 걱정할 필요없이 마스터 페이지와 병렬로 iframe에서 실행중인 프로세스를 가질 수 있으며 마스터 페이지의 JavaScript (가져온 JavaScript 포함)를 iframe.

예를 들어 부모 iframe egFunction()에서 iframe 콘텐츠가로드되면 부모 문서 를 호출 합니다 (비동기 부분).

parent.egFunction();

동적으로 iframe도 생성하므로 원하는 경우 기본 HTML 코드가 무료입니다.


1
이 설명은 내가 좋아하는 것보다 너무 짧았습니다. 이 기술을 수행하는 방법에 대해 자세히 설명하거나 코드를 보여주는 자습서 링크를 게시 할 수 있습니까?
oligofren

3

또 다른 가능한 방법은 자바 스크립트 환경에서 자바 스크립트 인터프리터를 사용하는 것입니다.

여러 개의 인터프리터를 작성하고 기본 스레드에서 실행을 제어하여 자체 환경에서 실행되는 각 스레드로 멀티 스레딩을 시뮬레이션 할 수 있습니다.

이 접근 방식은 웹 작업자와 다소 비슷하지만 통역사에게 브라우저 글로벌 환경에 대한 액세스 권한을 부여합니다.

나는 이것을 보여주기 위해 작은 프로젝트를 만들었다 .

이 블로그 게시물 에 대한 자세한 설명 .


1

자바 스크립트에는 스레드가 없지만 작업자가 있습니다.

공유 객체가 필요하지 않은 작업자는 좋은 선택 일 수 있습니다.

대부분의 브라우저 구현은 실제로 모든 코어에 작업자를 분산시켜 모든 코어를 활용할 수 있습니다. 여기서 데모를 볼 수 있습니다 .

나는 이것을 매우 쉽게하는 task.js 라는 라이브러리를 개발했습니다 .

task.js 모든 코어 (node.js 및 웹)에서 CPU 집약적 코드를 실행하기위한 단순화 된 인터페이스

예를 들면

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});

0

HTML5 사양 을 사용 하면 동일한 JS를 너무 많이 쓰거나 해킹을 찾을 필요가 없습니다.

HTML5에 도입 된 기능 중 하나 는 페이지의 성능에 영향을주지 않으면 서 다른 스크립트와 독립적으로 백그라운드에서 JavaScript를 실행하는 Web Worker 입니다.

거의 모든 브라우저에서 지원됩니다.

크롬-4.0+

IE-10.0+

모질라-3.5+

사파리-4.0+

오페라-11.5+

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.