JavaScript에서 멀티 스레딩을 수행 할 수있는 방법이 있습니까?
JavaScript에서 멀티 스레딩을 수행 할 수있는 방법이 있습니까?
답변:
최신 지원 정보는 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 4 및 WebKit 야간 에는 작업자 스레드가 있습니다.
Chrome 에는 Gears가 내장되어 있으므로 사용자의 확인 메시지가 필요하지만 스레드를 수행 할 수 있습니다 (Gears 플러그인이 설치된 모든 브라우저에서 작동하지만 웹 작업자에게 다른 API를 사용함).
IE8 및 IE9 는 Gears 플러그인이 설치된 스레드 만 수행 할 수 있습니다.
HTML5 JavaScript 이전에는 페이지 당 하나의 스레드 만 실행할 수있었습니다.
와 비동기 실행 시뮬레이션 일부 해키 방법 있었다 수율은 , setTimeout()
, setInterval()
, XMLHttpRequest
또는 이벤트 핸들러 (예와 본 게시물 마지막 참조 수율 및 setTimeout()
).
그러나 HTML5에서는 이제 Worker Threads를 사용하여 함수 실행을 병렬화 할 수 있습니다. 다음은 사용 예입니다.
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이 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()
과 같이 함수를 차례로 실행하는 것입니다 .
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>
보시다시피이 두 번째 방법은 매우 느리고 메인 스레드를 사용하여 기능을 실행하기 때문에 브라우저를 정지시킵니다.
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 개의 스레드를 만들어 숫자 추가를 계산하고 숫자를 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();
이 방법이 도움이 되길 바랍니다.
코드를 상태 머신으로 변환하여 효과적으로 스레딩을 에뮬레이트하는 컴파일러 인 Narrative JavaScript를 사용할 수 있습니다 . 단일 선형 코드 블록에 비동기 코드를 작성할 수있는 언어에 "yielding"연산자 ( '->'로 표기)를 추가하면됩니다.
원시 Javascript에서 할 수있는 최선의 방법은 몇 개의 비동기 호출 (xmlhttprequest)을 사용하는 것이지만 실제로는 스레딩이 아니며 매우 제한적이지 않습니다. Google Gears 는 브라우저에 여러 API를 추가하며 그 중 일부는 스레딩 지원에 사용될 수 있습니다.
AJAX를 사용할 수 없거나 사용하지 않으려면 iframe 또는 10을 사용하십시오! ;) 닷넷 AJAX 등의 브라우저 간 비교 가능한 문제 나 구문 문제에 대해 걱정할 필요없이 마스터 페이지와 병렬로 iframe에서 실행중인 프로세스를 가질 수 있으며 마스터 페이지의 JavaScript (가져온 JavaScript 포함)를 iframe.
예를 들어 부모 iframe egFunction()
에서 iframe 콘텐츠가로드되면 부모 문서 를 호출 합니다 (비동기 부분).
parent.egFunction();
동적으로 iframe도 생성하므로 원하는 경우 기본 HTML 코드가 무료입니다.
자바 스크립트에는 스레드가 없지만 작업자가 있습니다.
공유 객체가 필요하지 않은 작업자는 좋은 선택 일 수 있습니다.
대부분의 브라우저 구현은 실제로 모든 코어에 작업자를 분산시켜 모든 코어를 활용할 수 있습니다. 여기서 데모를 볼 수 있습니다 .
나는 이것을 매우 쉽게하는 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
});
HTML5 사양 을 사용 하면 동일한 JS를 너무 많이 쓰거나 해킹을 찾을 필요가 없습니다.
HTML5에 도입 된 기능 중 하나 는 페이지의 성능에 영향을주지 않으면 서 다른 스크립트와 독립적으로 백그라운드에서 JavaScript를 실행하는 Web Worker 입니다.
거의 모든 브라우저에서 지원됩니다.
크롬-4.0+
IE-10.0+
모질라-3.5+
사파리-4.0+
오페라-11.5+