JavaScript'in tek iş parçacıklı olduğu garanti ediliyor mu?


610

JavaScript'in tüm modern tarayıcı uygulamalarında tek iş parçacıklı olduğu biliniyor, ancak bu herhangi bir standartta belirtilmiş mi yoksa sadece geleneğe göre mi? JavaScript'in her zaman tek iş parçacıklı olduğunu varsaymak tamamen güvenli mi?


25
Muhtemelen tarayıcılar bağlamında. Ancak bazı programlar JS'yi en üst düzey bir dil olarak ele almanıza ve diğer C ++ kütüphaneleri için bağlamalar yapmanıza izin verir. Örneğin, flusspferd (JS - AWESOME BTW için C ++ bağlamaları) çok iş parçacıklı JS ile bazı şeyler yapıyordu. Bağlama bağlı.
NG.

Yanıtlar:


583

Bu iyi bir soru. “Evet” demek istiyorum. Yapamam.

JavaScript'in genellikle komut dosyalarına (*) görünür tek bir yürütme iş parçacığı olduğu düşünülür;

(*: tarayıcıların JS motorlarını tek bir OS iş parçacığı kullanarak gerçekten uygulayıp uygulamadığı ya da diğer sınırlı yürütme iş parçacıklarının WebWorkers tarafından tanıtılıp tanıtılmadığı sorusunu yok saymak.)

Ancak, gerçekte bu sinsi kötü yollarla tam olarak doğru değildir .

En yaygın durum acil olaylardır. Kodunuz bunlara neden olacak bir şey yaptığında tarayıcılar bunları tetikler:

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 hariç tüm sonuçlar . Bu olaylar yalnızca focus()doğrudan aradığınız için değil , aradığınız alert()veya bir açılır pencere veya odağı hareket ettiren başka bir şey açtığınız için de gerçekleşebilir.

Bu aynı zamanda başka olaylarla da sonuçlanabilir. Örneğin, bir i.onchangedinleyici ekleyin ve focus()çağrı odaklanmadan önce girdiye bir şey yazın ve günlük sırası, log in, change, blur, log outOpera ve onun log in, blur, log out, changebulunduğu IE (daha az açıklayıcı) hariç log in, change, log out, blur.

Benzer şekilde click(), bunu sağlayan bir öğeyi çağırmak , onclicktüm tarayıcılarda işleyiciyi hemen çağırır (en azından bu tutarlıdır!).

(Ben direkt kullanarak değilim on...burada olay işleyicisi özelliklerini ancak aynı ile olur addEventListenerve attachEvent.)

Ayrıca , provoke etmek için hiçbir şey yapmamış olmanıza rağmen, kodunuz işlenirken olayların tetiklenebileceği bir dizi koşul vardır . Bir örnek:

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>

Vur alertve kalıcı bir diyalog kutusu alacaksın. Bu diyaloğu kapatana kadar başka senaryo çalışmaz, değil mi? Hayır! Ana pencereyi yeniden boyutlandırdığınızda alert in, resize, alert outmetin alanına girersiniz.

Kalıcı bir iletişim kutusu varken bir pencereyi yeniden boyutlandırmanın imkansız olduğunu düşünebilirsiniz, ancak öyle değil: Linux'ta pencereyi istediğiniz kadar yeniden boyutlandırabilirsiniz; Windows'da o kadar kolay değildir, ancak ekran çözünürlüğünü pencerenin sığmadığı bir büyükten daha küçük bir düzeye değiştirerek yeniden boyutlandırılmasına neden olabilirsiniz.

Kullanıcı, tarayıcı ile aktif bir etkileşime sahip olmadığında, komut dosyası işlendiği için tetiklenebilecek yalnızca resize(ve muhtemelen birkaç tane daha gibi scroll) düşünebilirsiniz . Ve tek pencereler için haklı olabilirsiniz. Ancak, çapraz pencere komut dosyası oluşturduğunuzda bunların hepsi çöpe gider. Herhangi biri meşgulken tüm pencereleri / sekmeleri / çerçeveleri engelleyen Safari dışındaki tüm tarayıcılar için, ayrı bir yürütme iş parçacığında çalışan ve ilgili olay işleyicilerinin çalışmasına neden olan başka bir belgenin kodundan bir belgeyle etkileşimde bulunabilirsiniz. ateş.

Komut dosyası işlenirken oluşturulabilecek olayların oluşturulabileceği yerler yükseltilebilir:

  • Modal pop-up (zaman alert, confirm, prompt), açık tüm tarayıcılarda ama Opera'da vardır;

  • sırasında showModalDialogbunu destekleyen tarayıcılar üzerinde;

  • "Bu sayfadaki bir komut dosyası meşgul olabilir ..." iletişim kutusu, komut dosyasının çalışmaya devam etmesine izin verseniz bile, yeniden boyutlandırma ve bulanıklaştırma gibi olayların tetiklenmesine izin verir ve komut dosyası bir ortalamanın ortasındayken bile işlenir Meşgul döngü, Opera hariç.

  • Bir süre önce benim için, IE'de Sun Java Eklentisi ile, bir uygulamadaki herhangi bir yöntemi çağırmak olayların tetiklenmesine ve komut dosyasının tekrar girilmesine izin verebilir. Bu her zaman zamanlamaya duyarlı bir hataydı ve Sun'ın o zamandan beri düzeltmesi mümkündür (kesinlikle umarım).

  • muhtemelen daha fazla. Bunu test ettiğimden beri bir süre geçti ve tarayıcılar o zamandan beri karmaşıklık kazandı.

Özetle, JavaScript çoğu kullanıcı için, çoğu zaman katı bir olaya dayalı tek bir yürütme iş parçacığına sahip gibi görünür. Gerçekte böyle bir şey yok. Bunun ne kadarının bir hata olduğu ve ne kadar kasıtlı bir tasarım olduğu açık değildir, ancak karmaşık uygulamalar, özellikle çapraz pencere / çerçeve komut dosyası uygulamaları yazıyorsanız, sizi ısırması için her şans vardır - ve aralıklı olarak, hata ayıklaması zor yollar.

En kötüsü en kötüsüne gelirse, tüm olay yanıtlarını indirerek eşzamanlılık sorunlarını çözebilirsiniz. Bir olay geldiğinde, onu bir kuyruğa bırakın ve daha sonra sırayla, bir setIntervalişlevle kuyrukla ilgilenin . Karmaşık uygulamalar tarafından kullanılmak istediğiniz bir çerçeve yazıyorsanız, bunu yapmak iyi bir hareket olabilir. postMessageayrıca gelecekte çapraz belge komut dosyası yazma ağrısını hafifletecektir.


14
@JP: Şahsen hemen bilmek istemiyorum çünkü kodumun yeniden girildiğine dikkat etmeliyim, bulanıklık kodumu çağırmanın bazı dış kodların bağlı olduğu durumu etkilemeyeceği anlamına geliyor. Bulanıklaştırmanın mutlaka her birini yakalamak için beklenmedik bir yan etki olduğu çok fazla durum vardır. Ne yazık ki olsa bile Ve yapmak istiyorum, güvenilir değil! Kodunuz tarayıcıya denetimi döndürdükten blur sonra IE tetiklenir .
bobince

107
Javascript tek iş parçacıklı. Alert () üzerinde yürütmenizi durdurmak, olay dizisinin pompalama olaylarını durdurduğu anlamına gelmez. Uyarı ekrandayken komut dosyanızın uyuduğu anlamına gelir, ancak ekranı çizmek için pompalama olaylarını sürdürmesi gerekir. Bir uyarı olduğunda olay pompası çalışıyor, yani olayları göndermeye devam etmek tamamen doğru. En iyi ihtimalle bu, javascript'te olabilecek işbirlikçi bir iş parçacığı olduğunu gösterir, ancak tüm bu davranış, olay pompasına daha sonra işlenecek ve daha sonra işlenecek bir olayı ekleyen bir işlevle açıklanabilir.
chubbsondubs

34
Ancak, işbirlikçi diş açmanın hala tek iş parçacıklı olduğunu unutmayın. İki şey aynı anda gerçekleşemez, bu da çoklu iş parçacığının determinizme izin verdiği ve enjekte ettiği şeydir. Açıklanan her şey deterministiktir ve bu tür konular hakkında iyi bir hatırlatmadır. Analizde iyi iş @bobince
chubbsondubs

94
Chubbard haklı: JavaScript tek iş parçacıklı. Bu, çoklu iş parçacığı değil, tek bir iş parçacığında senkronize mesaj gönderme örneğidir. Evet, yığını duraklatmak ve olay gönderimini devam ettirmek mümkündür (örn. Alert ()), ancak gerçek çok iş parçacıklı ortamlarda meydana gelen erişim sorunları türleri gerçekleşemez; örneğin, bir test ve hemen bir sonraki atama arasında asla değişken bir değişiklik değeriniz olmayacaktır, çünkü iş parçacığınız keyfi olarak kesilemez. Bu yanıtın sadece karışıklığa neden olacağından korkuyorum.
Kris Giesing

19
Evet, ancak kullanıcı girişini bekleyen bir engelleme işlevinin herhangi iki ifade arasında gerçekleşebileceği göz önüne alındığında, OS düzeyinde iş parçacıklarının size getirdiği tüm tutarlılık sorunlarına sahip olabilirsiniz. JavaScript motorunun gerçekten birden fazla işletim sistemi iş parçacığında çalışıp çalışmadığı çok az önemlidir.
bobince

115

Evet diyorum - çünkü bir tarayıcının javascript motoru senkronize olmayan bir şekilde çalıştırmak için neredeyse tüm mevcut (en azından tüm önemsiz olmayan) javascript kodu kırılır.

HTML5'in , temel Javascript'e çoklu iş parçacığını tanıtan Web Workers'ı (çoklu iş parçacığı javascript kodu için açık, standart bir API) zaten belirttiği gerçeğini de ekleyin .

( Diğerleri yorum yazanlara Not: rağmen setTimeout/setIntervaltek tek - - onlar hala tek bir zaman çizgisi boyunca yürütülür, HTTP isteği olayları (XHR) ve UI olayları (tıklama, odak, vs.) çok threadedness bir ham izlenim sağlamak Onload bir süre - bu nedenle, yürütme sırasını önceden bilmesek bile, bir olay işleyicinin, zamanlanmış işlevin veya XHR geri aramasının yürütülmesi sırasında dış koşulların değişmesi konusunda endişelenmenize gerek yoktur.)


21
Katılıyorum. Tarayıcıda Javascript'e çoklu iş parçacığı eklenirse, tıpkı tüm zorunlu dillerde olduğu gibi bazı açık API (örn. Web Çalışanları) aracılığıyla olacaktır . Mantıklı olan tek yol budur.
Dean Harding

1
Bir tane olduğunu unutmayın. ana JS iş parçacığı, ama bazı şeyler paralel olarak tarayıcıda çalıştırılır. Bu sadece çok parçacıklı bir izlenim değil. İstekler aslında paralel olarak yürütülür. JS'de tanımladığınız dinleyiciler tek tek çalıştırılır, ancak istekler gerçekten paraleldir.
Nux

16

Evet, setInterval ve xmlhttp geri çağrıları gibi eşzamansız API'lardan herhangi birini kullanırken eşzamanlı programlama sorunlarından bazılarını (özellikle yarış koşulları) hala yaşayabilirsiniz.


10

Evet, ancak Internet Explorer 9 ana iş parçacığında yürütülmeye hazırlanırken Javascript'inizi ayrı bir iş parçacığında derleyecektir. Bu bir programcı olarak sizin için hiçbir şeyi değiştirmez.


8

Söyleyebilirm şartname engellemez birilerini bir motor yaratarak o çalışır üzerinde javascript birden çok iş parçacığı paylaşılan nesne durumuna erişmek için eşitleme gerçekleştirmek için kod gerektiren.

Tek iş parçacıklı, engellemeyen paradigmanın , ui'nin asla engellememesi gereken tarayıcılarda javascript çalıştırma ihtiyacından kaynaklandığını düşünüyorum .

Nodejs tarayıcıların yaklaşımını izledi .

Ancak Rhino motoru, farklı iş parçacıklarında js kodu çalıştırmayı destekler . İcralar bağlamı paylaşamaz, ancak kapsamı paylaşabilirler. Bu özel durum için belgeler şunları belirtir:

... "Rhino, JavaScript nesnelerinin özelliklerine erişimin iş parçacıkları arasında atomik olduğunu garanti eder, ancak aynı kapsamda aynı anda yürütülen komut dosyaları için daha fazla garanti vermez. İki komut dosyası aynı kapsamı aynı anda kullanırsa, komut dosyaları paylaşılan değişkenlere erişimi koordine etmekten sorumludur . "

Rhino belgelerini okuduğumdan, birisinin yeni javascript ipliklerini de üreten bir javascript api yazmasının mümkün olabileceği sonucuna varıyorum, ancak api gergiye özgü olacaktı (örneğin, düğüm yalnızca yeni bir süreç oluşturabilir).

Javascript içinde birden fazla iş parçacığı destekleyen bir motor için bile çoklu iş parçacığı veya engelleme dikkate almaz komut dosyaları ile uyumluluk olması gerektiğini düşünüyorum.

Concearning tarayıcıları ve nodejs şekilde ben öyle bakın:

    1. Is tüm js kodu idam tek iplik ? : Evet.
    1. Can js neden kodu diğer konuları çalıştırmak için ? : Evet.
    1. Bu konu Can mutasyona js yürütme içeriği :? Hayır. Ama onlar can (doğrudan / dolaylı ()?) Ekleme olay kuyruğuna hangi dinleyiciler olabilir yürütme içeriği mutasyona . Ama aldanmayın, dinleyiciler tekrar ana iş parçacığında atomik olarak ilerler .

Yani, tarayıcılar ve nodejs (ve muhtemelen diğer birçok motorda) javascript çok iş parçacıklı değildir, ancak motorların kendisidir .


Web çalışanları hakkında güncelleme:

Web çalışanlarının varlığı, birisinin javascript'te ayrı bir iş parçacığında çalışacak kod oluşturabileceği anlamında, javascript'in çok iş parçacıklı olabileceğini daha da haklı çıkarır.

Ancak, web çalışanları , yürütme bağlamını paylaşabilen geleneksel iş parçacıklarının sorunlarını azaltmazlar. Yukarıdaki kural 2 ve 3 hala geçerlidir , ancak bu kez iş parçacığı kodu javascript kullanıcı (js kodu yazar) tarafından oluşturulur.

Dikkate alınması gereken tek şey , verimlilik ( eşzamanlılık değil ) açısından ortaya çıkan ipliklerin sayısıdır . Aşağıya bakınız:

Hakkında iplik güvenliği :

İşçi arabirimi gerçek OS düzeyindeki iş parçacıklarını ortaya çıkarır ve dikkatli programcılar, eşzamanlılığın dikkatli değilseniz kodunuzda "ilginç" etkilere neden olabileceğinden endişe edebilir.

Bununla birlikte, web çalışanları diğer iş parçacıkları ile iletişim noktalarını dikkatlice kontrol ettikleri için eşzamanlılık sorunlarına neden olmak çok zordur . Güvenli olmayan bileşenlere veya DOM'ye erişim yok. Ve belirli verileri serileştirilmiş nesnelerden bir iş parçacığına girip çıkarmanız gerekir. Bu nedenle, kodunuzda sorunlara neden olmak için gerçekten çok çalışmalısınız.


PS

Teorinin yanı sıra, her zaman kabul edilen cevapta tanımlanan olası köşe vakaları ve hatalar hakkında hazırlıklı olun


7

JavaScript / ECMAScript bir ana bilgisayar ortamında yaşamak için tasarlanmıştır. Yani, ana bilgisayar ortamı belirli bir komut dosyasını ayrıştırmaya ve yürütmeye karar vermedikçe ve JavaScript'in gerçekten kullanışlı olmasına izin veren ortam nesneleri (tarayıcılardaki DOM gibi) sağlamadığı sürece JavaScript aslında hiçbir şey yapmaz .

Belirli bir işlev veya komut dosyası satır satır yürütmek ve JavaScript için garanti düşünüyorum. Ancak, bir ana bilgisayar ortamı aynı anda birden çok komut dosyası yürütebilir. Veya, bir ana bilgisayar ortamı her zaman çoklu iş parçacığı sağlayan bir nesne sağlayabilir. setTimeoutve setIntervalbir eşzamanlılık (tam eşzamanlılık olmasa bile) bir yol sağlayan bir ana bilgisayar ortamına örnekler veya en azından sözde örneklerdir.


7

Aslında, bir üst pencere kendi yürütme iş parçacıklarının çalıştığı çocuk veya kardeş pencere veya çerçevelerle iletişim kurabilir.


6

@Bobince gerçekten opak bir cevap veriyor.

Már Örlygsson'un cevabından vazgeçen Javascript, bu basit gerçek nedeniyle her zaman tek iş parçacıklıdır: Javascript'teki her şey tek bir zaman çizelgesi boyunca yürütülür.

Bu, tek iş parçacıklı bir programlama dilinin katı tanımıdır.


4

Hayır.

Buradaki kalabalığa karşı çıkıyorum, ama bana katlan. Tek bir JS betiğinin etkili bir şekilde tek iş parçacıklı olması amaçlanmıştır , ancak bu farklı yorumlanamayacağı anlamına gelmez.

Diyelim ki aşağıdaki koda sahipsiniz ...

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

Bu, döngünün sonunda, listenin dizin karesi olan 10000 girişe sahip olması beklenirken yazılır, ancak VM, döngünün her yinelemesinin diğerini etkilemediğini fark edebilir ve iki iş parçacığı kullanarak yeniden yorumlayabilir.

İlk iş parçacığı

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

İkinci iş parçacığı

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

Burada basitleştiriyorum, çünkü JS dizileri aptal bellek parçalarından daha karmaşıktır, ancak bu iki komut dosyası diziye iş parçacığı güvenli bir şekilde girişler ekleyebiliyorsa, o zaman her ikisi de yürütülürken tek iş parçacıklı sürümle aynı sonucu verir.

Bunun gibi paralelleştirilebilir kod algılayan herhangi bir VM'nin farkında olmasam da, gelecekte bazı durumlarda daha fazla hız sunabileceğinden, JIT VM'leri için gelecekte ortaya çıkabileceği düşünülmektedir.

Bu kavramı daha ileri götürerek, VM'nin çok iş parçacıklı koda nasıl dönüştürüleceğini bilmesini sağlamak için kodun açıklanabilmesi mümkündür.

// 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 Çalışanları Javascript'e geldiğinden beri, bu ... çirkin sistemin ortaya çıkması pek olası değildir, ancak Javascript'in geleneklere göre tek iş parçacıklı olduğunu söylemenin güvenli olduğunu düşünüyorum.


2
Çoğu dil tanımı etkili bir şekilde tek iş parçacıklı olacak şekilde tasarlanmıştır ve etki özdeş olduğu sürece çoklu iş parçacığına izin verildiğini belirtir. (örn. UML)
jevon

1
Sadece mevcut ECMAScript eşzamanlı ECMAScript yürütme bağlamları için (tartışmasız aynı C için söylenebilir düşünüyorum) hiçbir hüküm vermedi çünkü cevap kabul etmeliyim . Daha sonra, bu cevap gibi, eşzamanlı iş parçacıklarının paylaşılan durumu değiştirebildiği herhangi bir uygulamanın bir ECMAScript uzantısı olduğunu iddia ediyorum .
user2864740

3

Peki, Chrome çok işlemlidir ve her sürecin kendi Javascript koduyla uğraştığını düşünüyorum, ancak kodun bildiği kadarıyla "tek iş parçacıklı" dır.

Javascript'te çoklu iş parçacığı için herhangi bir destek yoktur, en azından açıkça değil, bu yüzden bir fark yaratmaz.


2

Küçük bir değişiklikle @ bobince örneğini denedim:

<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>

Bu nedenle, Çalıştır'a bastığınızda, uyarı açılır penceresini kapatıp "tek bir iş parçacığı" yaptığınızda şöyle bir şey görmelisiniz:

click begin
click end
result = 20, should be 20

Ancak bunu Windows'ta Opera veya Firefox'ta kararlı hale getirmeye ve ekranda uyarı açılır penceresiyle pencereyi simge durumuna küçültme / büyütmeye çalışırsanız, şöyle bir şey olacaktır:

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

Bu "multithreading" olduğunu söylemek istemiyorum, ama bazı kod parçası ben bunu beklemiyorsun benimle yanlış bir zamanda yürüttü ve şimdi bozuk bir durum var. Ve bu davranışı bilmek daha iyi.


-4

İki setTimeout işlevini iç içe yerleştirmeye çalışın ve çok iş parçacıklı davranırlar (yani dış zamanlayıcı, işlevini yerine getirmeden önce iç işlevin tamamlanmasını beklemez).


5
: Krom @James o okuyuculu ediliyor gördüğü durumlarda, bilmiyorum ... Bu doğru yolu yapar setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)(kayıt için yo dostum HER ZAMAN, ilk önce konsol günlük çıkışını gelmelidir)
Tor Valamo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.