JavaScriptはシングルスレッドであることが保証されていますか?


611

JavaScriptは、すべての最新のブラウザー実装でシングルスレッドであることが知られていますが、それは標準で指定されているのですか、それとも伝統によるものですか?JavaScriptが常にシングルスレッドであると仮定することは完全に安全ですか?


25
ブラウザのコンテキストでは、おそらく。ただし、一部のプログラムでは、JSを最上位のlangとして扱い、他のC ++ライブラリのバインディングを提供できます。たとえば、flusspferd(JSのC ++バインディング-AWESOME BTW)は、マルチスレッドJSで何らかの処理を行っていました。状況次第です。
NG。

回答:


583

それは良い質問です。「はい」と言いたいです。できません。

JavaScriptは通常、スクリプト(*)から見える単一の実行スレッドを持っていると見なされるため、インラインスクリプト、イベントリスナー、またはタイムアウトに入ると、ブロックまたは関数の最後から戻るまで、完全に制御できます。

(*:ブラウザが実際に1つの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()呼び出しがフォーカスを外す前に入力に何かを入力すると、ログの順序はlog in, change, blur, log outになります。log in, blur, log out, changelog 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以外のすべてのブラウザーでは、いずれかのブラウザーがビジー状態のときにすべてのウィンドウ、タブ、フレームをブロックします。別のドキュメントのコードからドキュメントを操作し、別の実行スレッドで実行して、関連するイベントハンドラーに火。

スクリプトがまだスレッド化されている間に、発生させることができるイベントを発生させることができる場所:

  • モーダルポップアップは(ときalertconfirmprompt)すべてのブラウザが、オペラでは、開いています。

  • 中にshowModalDialogそれをサポートするブラウザ上。

  • 「このページのスクリプトはビジー状態です...」ダイアログボックスでは、スクリプトの実行を継続することを選択した場合でも、スクリプトの途中でもサイズ変更やぼかしなどのイベントを起動して処理できます。 Operaを除いてビジーループ。

  • しばらく前の私にとって、Sun Java Pluginを使用するIEでは、アプレットの任意のメソッドを呼び出すと、イベントが発生し、スクリプトに再び入ることができました。これは常にタイミングの影響を受けやすいバグであり、Sunがそれを修正した可能性があります(私は確かにそう望んでいます)。

  • おそらくもっと。私がこれをテストしてからしばらく経ちましたが、ブラウザは複雑になっています。

要約すると、JavaScriptはほとんどの場合、ほとんどの場合、厳密なイベント駆動型の単一の実行スレッドを持っているように見えます。実際にはそのようなことはありません。これがどれだけバグであり、どの程度意図的に設計されているかは明らかではありませんが、複雑なアプリケーション、特にクロスウィンドウ/フレームスクリプティングアプリケーションを作成している場合、それが断続的に噛み付く可能性があります。デバッグが難しい方法。

最悪の事態が発生した場合、すべてのイベント応答を間接化することにより、同時実行性の問題を解決できます。イベントが入ってくると、それをキューに入れ、後でsetInterval関数内で順番にキューを処理します。複雑なアプリケーションで使用する予定のフレームワークを作成している場合は、これを行うとよいでしょう。postMessageまた、将来的にはクロスドキュメントスクリプティングの苦痛を和らげることが期待されます。


14
@JP:コードが再入可能であることに注意する必要があるため、個人的にはすぐに知りたくありません。ぼかしコードを呼び出しても、一部の外部コードが依存している状態には影響しません。ぼかしが予期せぬ副作用であり、必ずしもすべてを捉えることができない場合が多すぎます。そして、残念ながらあなたがそれを望んでも、それは信頼できません!コードがブラウザに制御を返したblur 後に IEが起動します。
ボビンス、2010

107
Javascriptはシングルスレッドです。alert()で実行を停止しても、イベントスレッドがイベントの送出を停止するわけではありません。アラートが画面に表示されている間はスクリプトがスリープしていることを意味しますが、画面を描画するためにイベントを送り続ける必要があります。アラートがアップしている間、イベントポンプは実行中です。つまり、イベントを送信し続けることは完全に正しいことです。これはせいぜいJavaScriptで発生する可能性のある協調スレッドを示していますが、この動作はすべて、後で処理するイベントポンプにイベントを追加するだけでなく、後で処理する関数で説明できます。
chubbsondubs 2010

34
ただし、協調スレッドは依然としてシングルスレッドであることを覚えておいてください。マルチスレッドが可能にし、非決定性を注入するという2つのことは同時に発生することはできません。説明された内容はすべて確定的であり、これらのタイプの問題についての良い思い出させてくれます。@bobinceの分析は順調です
chubbsondubs

94
Chubbardは正解です。JavaScriptはシングルスレッドです。これはマルチスレッドの例ではなく、単一スレッドでの同期メッセージディスパッチです。はい、スタックを一時停止してイベントディスパッチを続行すること(たとえば、alert())は可能ですが、実際のマルチスレッド環境で発生するこの種のアクセス問題は発生しません。たとえば、スレッドを任意に中断できないため、テストとその直後の割り当てとの間で変数の変更値が発生することはありません。この反応が混乱を招くだけであるのではないかと恐れています。
クリスギーシング

19
はい。ただし、ユーザー入力を待機するブロッキング関数が2つのステートメントの間に発生する可能性があることを考えると、OSレベルのスレッドがもたらす一貫性の問題がすべて発生する可能性があります。JavaScriptエンジンが実際に複数のOSスレッドで実行されるかどうかは、あまり重要ではありません。
ボビンス2012

115

ブラウザーのJavaScriptエンジンが非同期に実行すると、事実上すべての(少なくともすべての重要な)JavaScriptコードが機能しなくなるためです。

加えて、HTML5が基本的なJavaScriptにマルチスレッドを導入するWebワーカー(マルチスレッドJavaScriptコード用の明示的で標準化されたAPI)をすでに指定しているという事実はほとんど無意味です。

他のコメント者へのメモ:にもかかわらずsetTimeout/setInterval、HTTPリクエストのオンロードイベント(XHR)、およびUIイベント(クリック、フォーカスなど)は、マルチスレッドの大まかな印象を提供します-それらはすべて単一のタイムラインに沿って実行されます時間-したがって、事前にその実行順序がわからなくても、イベントハンドラー、時間指定関数、またはXHRコールバックの実行中に外部条件が変化することを心配する必要はありません。)


21
同意する。ブラウザでJavascriptにマルチスレッドが追加される場合、すべての命令型言語と同様に、明示的なAPI(Web Workersなど)を介してマルチスレッド化されます。それが意味のある唯一の方法です。
ディーンハーディング

1
シングルがあることに注意してください。メインのJSスレッドですが、いくつかはブラウザで並行して実行されます。マルチスレッドの印象だけではありません。リクエストは実際には並行して実行されます。JSで定義するリスナーは1つずつ実行されますが、要求は本当に並列です。
木の実

16

はい。ただし、setIntervalやxmlhttpコールバックなどの非同期APIを使用すると、並行プログラミングの問題(主に競合状態)が発生する可能性があります。


10

はい、ただしInternet Explorer 9はメインスレッドでの実行に備えて別のスレッドでJavascriptをコンパイルします。ただし、これはプログラマーにとって何も変更しません。


8

私はと言うでしょう仕様は防ぐことはできませんから、誰かをエンジンの作成走る上でJavaScriptを複数のスレッドで共有オブジェクトの状態にアクセスするための同期を実行するためのコードを必要とし、。

シングルスレッドの非ブロッキングパラダイムは、UIがブロックしてはならないブラウザーでJavaScriptを実行する必要性から生まれたと思います。

Nodejsブラウザのアプローチに従いました。

ただし、Rhinoエンジンは、さまざまなスレッドでのjsコードの実行をサポートしています。実行はコンテキストを共有できませんが、スコープを共有できます。この特定のケースでは、ドキュメントには次のように記載されています。

...「Rhinoは、JavaScriptオブジェクトのプロパティへのアクセスがスレッド間でアトミックであることを保証しますが、同じスコープで同時に実行されるスクリプトを保証しません。2つのスクリプトが同時に同じスコープを使用する場合、スクリプトはシェア変数へのアクセスを調整する責任があります。」

Rhinoのドキュメントを読んだところ、誰かが新しいJavaScriptスレッドを生成するJavaScript APIを記述できる可能性があると結論付けましたが、APIはRhino固有です(たとえば、ノードは新しいプロセスしか生成できません)。

JavaScriptで複数のスレッドをサポートするエンジンであっても、マルチスレッドやブロッキングを考慮しないスクリプトとの互換性があるはずです。

Concearning ブラウザnodejs方法私はそれがある参照してください。

    1. あるすべてのjsで実行されるコードシングルスレッドは?:はい。
    1. jsコードは他のスレッドを実行させることができますか?:はい。
    1. これらのスレッドはjs実行コンテキストを変更できますか?:いいえ。ただし、リスナーは(直接/間接(?))イベントキューに追加でき、そこから リスナー実行コンテキストを変更できます。しかし、だまされてはいけません。リスナーは再びメインスレッドでアトミックに実行されます

そのため、ブラウザやnodejs(おそらく他の多くのエンジン)の場合、JavaScriptはマルチスレッド化されていませんが、エンジン自体はマルチスレッド化されています


Webワーカーに関する更新:

Webワーカーの存在は、別のスレッドで実行されるJavaScriptでコードを作成できるという意味で、JavaScriptをマルチスレッド化できることをさらに正当化します。

ただし、Webワーカーは、実行コンテキストを共有できる従来のスレッドの問題をカレーしません上記のルール2と3も引き続き適用されますが、今回はスレッド化されたコードがJavaScriptでユーザー(jsコードライター)によって作成されます。

考慮べき唯一のことは、効率性の観点から(並行性はなく)生成されたスレッドの数です。下記参照:

スレッドセーフについて

Workerインターフェースは実際のOSレベルのスレッドを生成します。注意深いプログラマーは、注意しないと、並行性がコードに「興味深い」影響を与える可能性があることを懸念するかもしれません。

ただし、ウェブワーカーは他のスレッドとの通信ポイントを注意深く制御しているため、並行性の問題を引き起こすことは実際には非常に困難です。スレッドセーフでないコンポーネントやDOMにはアクセスできません。また、シリアル化されたオブジェクトを介して、スレッドとの間で特定のデータを渡す必要があります。そのため、コードに問題を引き起こすために、本当に一生懸命働かなければなりません。


PS

理論に加えて、常に受け入れられた回答に記載されている可能性のあるコーナーケースとバグについて準備してください


7

JavaScript / ECMAScriptは、ホスト環境内で動作するように設計されています。つまり、ホスト環境が特定のスクリプトを解析して実行し、JavaScriptを実際に役立つようにする環境オブジェクト(ブラウザーのDOMなど)を提供しない限り、JavaScriptは実際には何も実行しません。

特定の関数またはスクリプトブロックは1行ずつ実行されると思いますが、これは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;
}

これは、ループの終わりまでに、リストには2乗されたインデックスである10000のエントリが必要であるという期待で書かれていますが、VMはループの各反復が他に影響しないことに気付き、2つのスレッドを使用して再解釈できます。

最初のスレッド

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

2番目のスレッド

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

JS配列はメモリのダムチャンクよりも複雑であるため、ここで簡略化していますが、これらの2つのスクリプトがスレッドセーフな方法で配列にエントリを追加できる場合は、両方が実行されるまでに実行されます。シングルスレッドバージョンと同じ結果。

このような並列化可能なコードを検出する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;
}

WebワーカーがJavascriptを使用するようになるので、これよりも醜いシステムが登場することはまずありませんが、Javascriptは伝統的にシングルスレッドであると言って間違いないと思います。


2
ただし、ほとんどの言語定義は効果的にシングルスレッド化されるように設計されており、効果が同じである限りマルチスレッド化が許可されていると述べています。(UMLなど)
jevon

1
現在のECMAScriptは同時ECMAScript実行コンテキストに対して何の準備もしていない(おそらくCについても同じことが言えると思いますが)ので、私は答えに同意する必要があります。次に、この回答のように、共有スレッドを変更できる同時スレッドが存在する実装はECMAScript 拡張であると主張します。
user2864740 2014

3

さて、Chromeはマルチプロセスであり、すべてのプロセスが独自のJavascriptコードを処理すると思いますが、コードが知る限り、それは「シングルスレッド」です。

少なくとも明示的にではなく、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

2つのsetTimeout関数を相互に入れ子にしてみてください。それらはマルチスレッドで動作します(つまり、外部タイマーは内部の関数が完了するのを待たずに、その関数を実行します)。


5
chromeはこれを正しい方法で行います。@ Jamesがマルチスレッド化されているのを見ると、dunnoは...:(setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)記録としては、常に最初に来る必要があり、その後コンソールログの出力になるはずです)
Tor Valamo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.