JavaScript는 단일 스레드로 보장됩니까?


610

JavaScript는 모든 최신 브라우저 구현에서 단일 스레드로 알려져 있지만 표준에 지정되어 있습니까? 아니면 전통에 따라 지정 되었습니까? JavaScript가 항상 단일 스레드라고 가정하는 것이 안전합니까?


25
아마도 브라우저의 맥락에서. 그러나 일부 프로그램에서는 JS를 최상위 랭으로 취급하고 다른 C ++ 라이브러리에 대한 바인딩을 제공 할 수 있습니다. 예를 들어 flusspferd (JS에 대한 C ++ 바인딩-AWESOME BTW)는 멀티 스레드 JS로 몇 가지 작업을 수행했습니다. 상황에 달려 있습니다.
NG.

답변:


583

그건 좋은 질문이야. 나는“예”라고 말하고 싶습니다. 난 못해

JavaScript는 일반적으로 단일 실행 스레드가 스크립트 (*)에 표시되는 것으로 간주되므로 인라인 스크립트, 이벤트 리스너 또는 시간 초과가 입력 될 때 블록 또는 함수의 끝에서 돌아올 때까지 완전히 제어 할 수 있습니다.

(* : 브라우저가 하나의 OS 스레드를 사용하여 JS 엔진을 실제로 구현하는지 또는 WebWorkers가 다른 제한된 실행 스레드를 도입하는지에 대한 질문은 무시합니다.)

그러나 실제로 는 비열한 방식으로 이것은 사실이 아닙니다 .

가장 일반적인 경우는 즉각적인 이벤트입니다. 코드에서 무언가를 유발할 때 브라우저는 즉시이를 실행합니다.

var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
    l.value+= 'blur\n';
};
setTimeout(function() {
    l.value+= 'log in\n';
    l.focus();
    l.value+= 'log out\n';
}, 100);
i.focus();
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">

log in, blur, log outIE를 제외한 모든 결과가 나타 납니다. 이러한 이벤트는 사용자가 focus()직접 전화했기 때문에 발생하는 것이 아니라 전화 alert()를 걸거나 팝업 창을 열었거나 포커스를 이동시키는 다른 모든 것 때문에 발생할 수 있습니다 .

다른 이벤트가 발생할 수도 있습니다. 예를 들어 추가 i.onchange전과 입력 청취자를 입력 뭔가를 focus()호출을 unfocuses 및 로그 순서는 log in, change, blur, log out그것이 어디 오페라를 제외 log in, blur, log out, change하고 IE 곳의 (더 적은 explicably) log in, change, log out, blur.

마찬가지로 click()제공하는 요소를 호출하면 onclick모든 브라우저에서 즉시 핸들러를 호출 합니다 (적어도 일관성이 있습니다!).

( on...여기 에서 직접 이벤트 핸들러 속성을 사용하고 있지만 addEventListener및 에서도 마찬가지 attachEvent입니다.)

도발하기 위해 아무것도하지 않았 음에도 불구하고 코드가 스레드되는 동안 이벤트가 발생할 수있는 상황이 많이 있습니다 . 예를 들면 :

var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
    l.value+= 'alert in\n';
    alert('alert!');
    l.value+= 'alert out\n';
};
window.onresize= function() {
    l.value+= 'resize\n';
};
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>

히트 alert하면 모달 대화 상자가 나타납니다. 대화를 취소 할 때까지 더 이상 스크립트가 실행되지 않습니다. 아니. 메인 창의 크기를 조정하면 alert in, resize, alert out텍스트 영역에 나타납니다.

모달 대화 상자가 열려있는 동안 창의 크기를 조정하는 것이 불가능하다고 생각할 수도 있지만 그렇지 않습니다. Linux에서는 원하는만큼 창의 크기를 조정할 수 있습니다. Windows에서는 그렇게 쉽지는 않지만 화면 해상도가 창에 맞지 ​​않는 더 큰 화면에서 더 작은 화면으로 변경하면 화면 크기가 조정됩니다.

스크립트가 스레드되어 있기 때문에 사용자가 브라우저와 활발한 상호 작용을하지 않을 때 발생할 수 resize있는 것은 유일하다고 생각할 수도 있습니다 scroll. 그리고 단일 창문의 경우 당신이 옳을 수도 있습니다. 그러나 크로스 윈도우 스크립팅을 수행하는 즉시 모든 기능이 작동합니다. 사용 중일 때 모든 창 / 탭 / 프레임을 차단하는 Safari 이외의 모든 브라우저의 경우 별도의 실행 스레드에서 실행되고 관련 이벤트 처리기가 발생하도록 다른 문서의 코드에서 문서와 상호 작용할 수 있습니다. 불.

스크립트가 여전히 스레드되는 동안 생성 될 수있는 이벤트가 발생할 수있는 위치는 다음과 같습니다.

  • 모달 팝업은 (시 alert, confirm, prompt), 열려있는 모든 브라우저하지만 오페라에있다;

  • showModalDialog를 지원하는 브라우저에;

  • 스크립트를 계속 실행하도록 선택하더라도 "이 페이지의 스크립트가 사용 중일 수 있습니다 ..."대화 상자가 표시됩니다. 스크립트가 중간에있는 동안에도 크기 조정 및 흐림 효과와 같은 이벤트가 발생하고 처리됩니다. Opera를 제외하고 사용 중입니다.

  • 얼마 전에 필자는 Sun Java Plugin을 사용하는 IE에서 애플릿의 메소드를 호출하면 이벤트가 발생하고 스크립트를 다시 입력 할 수 있습니다. 이것은 항상 타이밍에 민감한 버그였으며 썬이 그 이후로 수정했을 가능성이 있습니다 (확실히 바랍니다).

  • 아마 더. 이것을 테스트 한 지 오래되었습니다. 브라우저는 그 이후로 복잡해졌습니다.

요약하자면, 대부분의 사용자에게 JavaScript는 대부분 이벤트 중심의 단일 실행 스레드를 갖는 것으로 보입니다. 실제로는 그런 것이 없습니다. 이 중 얼마나 많은 부분이 단순히 버그인지, 고의적 인 디자인인지는 확실하지 않지만, 복잡한 응용 프로그램, 특히 크로스 윈도우 / 프레임 스크립팅 응용 프로그램을 작성하는 경우, 당신을 물릴 수있는 모든 기회가 있습니다. 디버그하기 어려운 방법.

최악의 상황이 최악의 경우 모든 이벤트 응답을 간접적으로 지정하여 동시성 문제를 해결할 수 있습니다. 이벤트가 들어 오면 이벤트를 큐에 버리고 나중에 setInterval함수 에서 큐를 순서대로 처리하십시오 . 복잡한 응용 프로그램에서 사용하려는 프레임 워크를 작성하는 경우이 작업을 수행하는 것이 좋습니다. postMessage또한 앞으로 크로스 문서 스크립팅의 고통을 진정으로 달래줄 것입니다.


14
@ JP : 개인적으로 직접 알고 싶지 않습니다. 왜냐하면 내 코드가 다시 입력되는지, 블러 코드를 호출해도 일부 외부 코드가 의존하는 상태에 영향을 미치지 않도록주의해야하기 때문에 즉시 알고 싶지 않습니다. 흐림 효과가 예상치 못한 부작용으로 너무 많아서 반드시 모든 것을 포착하는 경우가 너무 많습니다. 당신은 불행하게도 경우에도 그리고 어떻게 그것을 원하는, 그것은 신뢰할 수 없습니다입니다! IE 화재 blur 코드를 반환 브라우저로 제어 할 수 있습니다.
bobince

107
자바 스크립트는 단일 스레드입니다. alert ()에서 실행을 중단한다고해서 이벤트 스레드가 이벤트 펌핑을 중지한다는 의미는 아닙니다. 경고가 화면에 표시되는 동안 스크립트가 잠자고 있음을 의미하지만 화면을 그리려면 펌핑 이벤트를 계속해야합니다. 경보가 작동하는 동안 이벤트 펌프가 실행 중이므로 이벤트를 계속 전송하는 것이 전적으로 정확합니다. 기껏해야 이것은 자바 스크립트에서 발생할 수있는 협력적인 스레딩을 보여 주지만,이 모든 동작은 이벤트 펌프에 이벤트를 추가하여 나중에 처리하는 것과 나중에 처리하는 기능으로 설명 할 수 있습니다.
chubbsondubs

34
그러나 협동 스레딩은 여전히 ​​단일 스레드입니다. 멀티 스레딩이 허용하고 비결 정성을 주입하는 것입니다. 설명 된 모든 것은 결정 론적이며 이러한 유형의 문제에 대해 잘 상기시켜줍니다. 분석 @bobince에 좋은 일이
chubbsondubs

94
Chubbard가 옳습니다. JavaScript는 단일 스레드입니다. 이것은 멀티 스레딩의 예가 아니라 단일 스레드에서 동기식 메시지 디스패치입니다. 예. 스택을 일시 정지하고 이벤트 디스패치를 ​​계속할 수 있지만 (예 : alert ()) 진정한 멀티 스레드 환경에서 발생하는 액세스 문제의 종류는 발생할 수 없습니다. 예를 들어, 스레드는 임의로 중단 될 수 없기 때문에 테스트와 즉시 후속 할당간에 변수 변경 값이 없습니다. 나는이 반응이 혼란을 야기 할 뿐이라고 우려한다.
Kris Giesing

19
예. 그러나 사용자 입력을 기다리는 블로킹 함수가 두 명령문 사이에서 발생할 수 있다는 점을 감안하면 OS 레벨 스레드가 가져 오는 모든 일관성 문제가있을 수 있습니다. JavaScript 엔진이 실제로 여러 OS 스레드에서 실행되는지 여부는 거의 관련이 없습니다.
bobince 2012

115

브라우저의 자바 스크립트 엔진이 비동기 적으로 실행하면 거의 모든 기존 (사소하지 않은) 자바 스크립트 코드가 작동하지 않기 때문에 예라고 대답하고 싶습니다.

HTML5가 이미 멀티 스레딩을 기본 자바 스크립트에 도입하는 웹 워커 (멀티 스레딩 자바 스크립트 코드를위한 명시적이고 표준화 된 API)를 지정 한다는 사실 은 무의미합니다.

( 다른 사람들의 덧글에 대한 참고 : 비록 setTimeout/setInterval하나 - - 그들은 여전히 하나의 타임 라인을 따라 실행, HTTP 요청은 이벤트 (XHR), 및 UI 이벤트 (클릭, 초점 등) 멀티 쓰레드 특성의 조잡한 느낌을 제공을 온로드 시간-따라서 사전에 실행 순서를 모르더라도 이벤트 핸들러, 시간 함수 또는 XHR 콜백을 실행하는 동안 외부 조건이 변경되는 것에 대해 걱정할 필요가 없습니다.)


21
동의한다. 멀티 스레딩이 브라우저에서 Javascript에 추가 된 경우 모든 명령형 언어 와 마찬가지로 명시 적 API (예 : 웹 워커)를 통해 이루어 집니다. 그게 말이되는 유일한 방법입니다.
Dean Harding

1
싱글이 있습니다. 주요 JS 스레드, 그러나 몇 가지가 브라우저에서 병렬로 실행됩니다. 그것은 단지 다중 스레드의 인상이 아닙니다. 요청은 실제로 병렬로 실행됩니다. JS에서 정의한 리스너는 하나씩 실행되지만 요청은 실제로 병렬입니다.
Nux

16

예. setInterval 및 xmlhttp 콜백과 같은 비동기 API를 사용할 때 동시 프로그래밍 (주로 경쟁 조건) 문제가 여전히 발생할 수 있습니다.


10

예, Internet Explorer 9은 기본 스레드에서 실행하기 위해 별도의 스레드에서 Javascript를 컴파일합니다. 그래도 프로그래머에게는 아무런 변화가 없습니다.


8

나는 스펙이 누군가가 여러 스레드에서 자바 스크립트 를 실행 하는 엔진생성하는 것을 막지 못한다고 말할 것입니다. 에서 공유 객체 상태에 액세스하기 위해 코드를 동기화해야합니다.

단일 스레드 비 차단 패러다임브라우저 에서 자바 스크립트를 실행할 필요가 있다고 생각 합니다. ui가 차단해서는 안되는 에서 .

Nodejs브라우저의 접근 방식을 따랐습니다. .

그러나 Rhino 엔진 은 다른 스레드에서 js 코드 실행을 지원합니다 . 실행은 컨텍스트를 공유 할 수 없지만 범위를 공유 할 수 있습니다. 이 특정 사례의 경우 설명서에 다음과 같이 명시되어 있습니다.

... "Rhino는 JavaScript 객체의 속성에 대한 액세스가 스레드 전체에서 원자 성임을 보장하지만 동일한 범위에서 동시에 동일한 범위에서 실행되는 스크립트를 더 이상 보장하지 않습니다. 두 스크립트가 동일한 범위를 동시에 사용 하면 스크립트는 공유 변수에 대한 액세스 조정을 담당합니다 . "

Rhino 문서를 읽음으로써 누군가가 새로운 자바 스크립트 스레드를 생성하는 자바 스크립트 API를 작성할 수 있지만, API는 코뿔소에 따라 다릅니다 (예 : 노드는 새로운 프로세스 만 스폰 할 수 있음).

자바 스크립트에서 여러 스레드를 지원하는 엔진의 경우에도 멀티 스레딩 또는 차단을 고려하지 않는 스크립트와의 호환성이 있어야한다고 생각합니다.

Concearning 브라우저를 하고 nodejs 방법 나는 그것이 참조 :

    1. 되어 모든 JS의 코드는 실행 단일 스레드 ? : 예.
    1. JS 원인 코드를 다른 쓰레드가 실행 ? : 예.
    1. 이 스레드 수 의 mutate JS 실행 컨텍스트가 : 아니요 그러나받는 사람 수 (직접 / 간접적으로 ()?) APPEND 이벤트 큐에 있는 청취자 수있는 실행 컨텍스트를 변이 . 그러나 속지 마십시오. 리스너 는 기본 스레드에서 다시 원자 적으로 실행 됩니다.

따라서 브라우저 및 nodejs (및 아마도 다른 많은 엔진)의 경우 javascript는 다중 스레드가 아니지만 엔진 자체는 입니다.


웹 작업자에 대한 업데이트 :

웹 작업자의 존재는 누군가가 별도의 스레드에서 실행될 자바 스크립트로 코드를 만들 수 있다는 점에서 자바 스크립트가 멀티 스레드 될 수 있음을 더 정당화합니다.

그러나 웹 작업자는 실행 컨텍스트를 공유 할 수 있는 기존 스레드의 문제를 해결하지 않습니다 . 위의 규칙 2와 3은 여전히 ​​적용됩니다 되지만 이번에는 스레드 코드가 사용자 (js 코드 작성기)가 javascript로 작성됩니다.

고려해야 할 유일한 것은 효율성 (및 동시성이 아닌) 에서 생성 된 스레드 수입니다. 아님) 관점 . 아래를보십시오 :

나사산 안전 정보 :

Worker 인터페이스는 실제 OS 수준 스레드를 생성하며, 신중한 프로그래머는주의하지 않으면 동시성이 코드에 "흥미로운"영향을 줄 수 있다고 우려 할 수 있습니다.

그러나 웹 작업자는 다른 스레드와의 통신 지점 을 신중하게 제어 하므로 실제로 동시성 문제를 일으키는 것은 매우 어렵습니다 . 스레드로부터 안전하지 않은 구성 요소 나 DOM에 액세스 할 수 없습니다. 그리고 직렬화 된 객체를 통해 특정 데이터를 스레드 안팎으로 전달해야합니다. 따라서 코드에 문제를 일으키려면 정말 열심히 노력해야합니다.


추신

이론 외에도 항상 허용되는 답변 에 설명 된 가능한 코너 사례 및 버그에 대해 준비 하십시오.


7

JavaScript / ECMAScript는 호스트 환경 내에서 작동하도록 설계되었습니다. 즉, 호스트 환경이 주어진 스크립트를 구문 분석하고 실행하기로 결정하고 JavaScript가 실제로 유용한 브라우저 (예 : 브라우저의 DOM)가 될 수있는 환경 객체를 제공하지 않는 한 JavaScript는 실제로 아무것도 하지 않습니다 .

주어진 함수 또는 스크립트 블록이 한 줄씩 실행되며 JavaScript에서 보장된다고 생각합니다. 그러나 호스트 환경은 동시에 여러 스크립트를 실행할 수 있습니다. 또는 호스트 환경은 항상 멀티 스레딩을 제공하는 객체를 제공 할 수 있습니다. setTimeout그리고 setInterval예, 또는 적어도 의사 예에서, 호스트 환경의 (정확히 동시성 아니더라도) 일부 병행 할 수있는 방법을 제공한다.


7

실제로 부모 창은 자체 실행 스레드가 실행중인 자식 창 또는 형제 창 또는 프레임과 통신 할 수 있습니다.


6

@Bobince는 정말 불투명 한 답변을 제공합니다.

Már Örlygsson의 답변에서 벗어난 Javascript는 다음과 같은 간단한 사실 때문에 항상 단일 스레드입니다. Javascript의 모든 것이 단일 타임 라인을 따라 실행됩니다.

이것이 단일 스레드 프로그래밍 언어의 엄격한 정의입니다.


4

아니.

나는 여기 군중들과 맞서고 있지만 나와 함께 견뎌내십시오. 단일 JS 스크립트는 효과적으로 단일 스레드로 만들어졌지만 이것이 다르게 해석 될 수 없음을 의미하지는 않습니다.

다음 코드가 있다고 가정 해 봅시다 ...

var list = [];
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

이것은 루프가 끝날 때까지 목록에 인덱스 제곱 인 10000 개의 항목이 있어야한다고 기대하지만 VM은 루프의 각 반복이 다른 루프에 영향을 미치지 않으며 두 개의 스레드를 사용하여 재 해석 할 수 있음을 알 수 있습니다.

첫 번째 실

for (var i = 0; i < 5000; i++) {
  list[i] = i * i;
}

두 번째 실

for (var i = 5000; i < 10000; i++) {
  list[i] = i * i;
}

JS 배열이 멍청한 메모리 청크보다 복잡하기 때문에 여기서 단순화하고 있습니다. 그러나이 두 스크립트가 스레드 안전 방식으로 배열에 항목을 추가 할 수 있다면 두 가지가 모두 실행될 때까지 단일 스레드 버전과 동일한 결과입니다.

이와 같은 병렬화 가능한 코드를 감지하는 VM을 모르지만 JIT VM의 경우 향후에 더 빠른 속도를 제공 할 수 있기 때문에 향후에 존재할 가능성이 높습니다.

이 개념을 더 발전 시키면 VM에 멀티 스레드 코드로 변환 할 항목을 알려주기 위해 코드에 주석을 달 수 있습니다.

// like "use strict" this enables certain features on compatible VMs.
"use parallel";

var list = [];

// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

웹 워커가 Javascript를 사용하고 있기 때문에이 시스템이 더 못 생겨 질 가능성은 없지만 Javascript가 전통적으로 단일 스레드라고 말하는 것이 안전하다고 생각합니다.


2
대부분의 언어 정의는 효과적으로 단일 스레드로 설계되었으며 효과가 동일한 한 멀티 스레딩이 허용됩니다. (예 : UML)
제번

1
현재 ECMAScript가 동시 ECMAScript 실행 컨텍스트에 대한 프로비저닝을 제공하지 않기 때문에 대답에 동의해야합니다 (C에 대해 동일하다고 말할 수는 있지만) . 그런 다음이 답변과 같이 공유 스레드를 수정할 수있는 동시 스레드가있는 모든 구현은 ECMAScript 확장 이라고 주장합니다 .
user2864740

3

글쎄, 크롬은 다중 프로세스이며 모든 프로세스가 자체 자바 스크립트 코드를 처리한다고 생각하지만 코드가 아는 한 "단일 스레드"입니다.

다중 스레딩을 위해 Javascript에서 적어도 명시 적으로 지원하지 않으므로 차이가 없습니다.


2

@bobince의 예제를 약간 수정하여 시도했습니다.

<html>
<head>
    <title>Test</title>
</head>
<body>
    <textarea id="log" rows="20" cols="40"></textarea>
    <br />
    <button id="act">Run</button>
    <script type="text/javascript">
        let l= document.getElementById('log');
        let b = document.getElementById('act');
        let s = 0;

        b.addEventListener('click', function() {
            l.value += 'click begin\n';

            s = 10;
            let s2 = s;

            alert('alert!');

            s = s + s2;

            l.value += 'click end\n';
            l.value += `result = ${s}, should be ${s2 + s2}\n`;
            l.value += '----------\n';
        });

        window.addEventListener('resize', function() {
            if (s === 10) {
                s = 5;
            }

            l.value+= 'resize\n';
        });
    </script>
</body>
</html>

따라서 실행을 누르고 경고 팝업을 닫고 "단일 스레드"를 수행하면 다음과 같은 내용이 표시됩니다.

click begin
click end
result = 20, should be 20

그러나 Windows에서 Opera 또는 Firefox에서 안정적으로 실행하고 화면에 경고 팝업이있는 창을 최소화 / 최대화하면 다음과 같은 결과가 나타납니다.

click begin
resize
click end
result = 15, should be 20

나는 이것이 "멀티 스레딩"이라고 말하고 싶지 않지만 이것을 예상하지 못한 코드 조각이 잘못된 시간에 실행되어 현재 상태가 손상되었습니다. 그리고이 행동에 대해 아는 것이 좋습니다.


-4

두 개의 setTimeout 함수를 서로 중첩 시키려고하면 멀티 스레드로 작동합니다 (즉, 외부 타이머는 기능을 실행하기 전에 내부 타이머가 완료 될 때까지 기다리지 않습니다).


5
크롬은 이것을 올바른 방법으로 수행합니다. @James가 멀티 스레드 된 것을 본 곳 dunno ... : setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)(기록을 위해 yo dawg는 항상 먼저 나와야하며 콘솔 로그 출력)
Tor Valamo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.