Scala oyuncuları: almak vs tepki vermek


110

Öncelikle, oldukça fazla Java deneyimim olduğunu söylememe izin verin, ancak son zamanlarda işlevsel dillerle ilgilenmeye başladım. Son zamanlarda çok güzel bir dil gibi görünen Scala'ya bakmaya başladım.

Bununla birlikte, Scala'nın Scala'da Programlamadaki Aktör çerçevesi hakkında okuyorum ve anlamadığım bir şey var. Bölüm 30.4'te, reactyerine receivekullanmanın, iş parçacığını yeniden kullanmayı mümkün kıldığını söylüyor; bu, JVM'de iş parçacıkları pahalı olduğu için performans için iyidir.

Bu, reactyerine aramayı hatırladığım sürece istediğim receivekadar Oyuncuya başlayabileceğim anlamına mı geliyor ? Scala'yı keşfetmeden önce, Erlang ile oynuyordum ve Programming Erlang'ın yazarı, 200.000'den fazla işlemi ter dökmeden üretmekle övünüyor. Bunu Java dizileriyle yapmaktan nefret ederim. Erlang (ve Java) ile karşılaştırıldığında Scala'da ne tür sınırlamalara bakıyorum?

Ayrıca, bu iş parçacığı yeniden kullanımı Scala'da nasıl çalışır? Basit olması için, sadece bir iş parçacığım olduğunu varsayalım. Başladığım tüm aktörler bu dizide sıralı olarak mı çalışacak yoksa bir tür görev değiştirme gerçekleşecek mi? Örneğin, birbirine pinpon mesajları gönderen iki oyuncu başlatırsam, aynı ileti dizisinde başladıkları takdirde kilitlenme riskini alır mıyım?

Scala'da Programlama'ya göre , oyuncuları kullanmak react, kullanmaktan daha zor receive. reactGeri dönmediği için bu kulağa mantıklı geliyor . Ancak kitap, reactkullanarak bir döngüyü nasıl içine koyabileceğinizi göstermeye devam ediyor Actor.loop. Sonuç olarak, alırsınız

loop {
    react {
        ...
    }
}

ki bana oldukça benziyor

while (true) {
    receive {
        ...
    }
}

kitabın önceki bölümlerinde kullanılan. Yine de kitap "uygulamada programların en az birkaçına ihtiyaç duyacağını" söylüyor receive. Peki burada neyi özlüyorum? Geri dönmekten başka ne yapabilir receiveki react? Ve neden umursuyorum?

Son olarak, anlamadığım şeyin özüne reactgelince : kitap, kullanmanın iş parçacığını yeniden kullanmak için çağrı yığınını atmayı nasıl mümkün kıldığından bahsetmeye devam ediyor . Bu nasıl çalışıyor? Çağrı yığınını atmak neden gereklidir? Ve bir işlev bir istisna ( react) atarak sona erdiğinde , ancak return ( ) ile sona erdiğinde çağrı yığını neden iptal edilebilir receive?

Scala'da Programlamanın buradaki bazı temel konuları gözden kaçırdığı izlenimine sahibim , bu utanç verici, çünkü aksi halde gerçekten mükemmel bir kitap.


Yanıtlar:


78

Birincisi, bekleyen her oyuncu receivebir konu işliyor. Asla bir şey almazsa, bu iş parçacığı hiçbir şey yapmaz. Bir aktör, bir reactşey alana kadar herhangi bir iş parçacığı işgal etmez. Bir şey aldığında, ona bir evre tahsis edilir ve içinde başlatılır.

Şimdi, başlatma kısmı önemlidir. Bir alıcı evrenin bir şey döndürmesi beklenir, tepki veren bir evre değildir. Dolayısıyla, sonun sonundaki önceki yığın durumu reacttamamen atılabilir ve tamamen atılır. Yığın durumunu kaydetmeye veya geri yüklemeye gerek kalmaması, iş parçacığının başlamasını hızlandırır.

Neden birini veya diğerini isteyebileceğiniz çeşitli performans nedenleri vardır. Bildiğiniz gibi, Java'da çok fazla iş parçacığına sahip olmak iyi bir fikir değil. Öte yandan, bir aktörü bir diziye yapmadan önce eklemeniz reactgerektiğinden, receivebir mesaja ondan daha hızlıdır react. Dolayısıyla, çok sayıda mesaj alan ancak onunla çok az şey yapan aktörleriniz varsa, ek gecikme, reactamacınız için çok yavaşlayabilir.


21

Cevap "evet" - eğer oyuncularınız kodunuzdaki hiçbir şeyi engellemiyorsa ve kullanıyorsanız react, "eşzamanlı" programınızı tek bir iş parçacığı içinde çalıştırabilirsiniz ( actors.maxPoolSizeöğrenmek için sistem özelliğini ayarlamayı deneyin ).

Çağrı yığınını atmanın neden gerekli olduğunun daha açık nedenlerinden biri, aksi takdirde loopyöntemin bir StackOverflowError. Olduğu gibi, çerçeve daha sonra yöntem aracılığıyla tekrar çalıştıran döngü kodu tarafından yakalanan reacta atarak a'yı akıllıca bitirir .SuspendActorExceptionreactandThen

İçindeki mkBodyyönteme Actorve ardından seqdöngünün kendini nasıl yeniden planladığını görmek için yönteme bir göz atın - son derece akıllıca şeyler!


20

Bu "yığını atmak" ifadeleri bir süredir kafamı karıştırdı ve sanırım şimdi anlıyorum ve şimdi anladığım bu. "Alma" durumunda, mesajda özel bir iş parçacığı engelleme vardır (bir monitörde object.wait () kullanarak) ve bu, tüm iş parçacığı yığınının mevcut olduğu ve bir mesaj alındığında "bekleme" noktasından devam etmeye hazır olduğu anlamına gelir. İleti. Örneğin, aşağıdaki koda sahipseniz

  def a = 10;
  while (! done)  {
     receive {
        case msg =>  println("MESSAGE RECEIVED: " + msg)
     }
     println("after receive and printing a " + a)
  }

iş parçacığı, mesaj alınana kadar alma çağrısında bekler ve ardından devam eder ve "10 alındıktan ve yazdırıldıktan sonra" mesajını ve iplik bloke edilmeden önce yığın çerçevesindeki "10" değerini yazdırır.

Reaksiyon durumunda, böyle bir tahsisli iş parçacığı yoktur, react yönteminin tüm yöntem gövdesi, bir kapanış olarak yakalanır ve bir mesajı alan karşılık gelen aktör üzerinde bazı gelişigüzel iş parçacığı tarafından yürütülür. Bu, yalnızca tek başına kapanış olarak yakalanabilen ifadelerin yürütüleceği anlamına gelir ve "Hiçbir Şey" in dönüş türü burada devreye girer. Aşağıdaki kodu düşünün

  def a = 10;
  while (! done)  {
     react {
        case msg =>  println("MESSAGE RECEIVED: " + msg)
     }
     println("after react and printing a " + a) 
  }

React'in dönüş türü void olsaydı, "react" çağrısından sonra ifadelere sahip olmanın yasal olduğu anlamına gelir (örnekte "tepki verdikten ve 10 yazdırdıktan sonra" mesajı yazdıran println ifadesi), ancak gerçekte sadece "react" yönteminin gövdesi yakalandığı ve daha sonra (bir mesaj geldiğinde) yürütülmek üzere sıralandığı için asla yürütülmez. React sözleşmesinin dönüş türü "Nothing" olduğundan, react'i izleyen herhangi bir ifade olamaz ve yığını sürdürmek için hiçbir neden yoktur. Yukarıdaki örnekte, "a" değişkeninin, react çağrılarının hiç yürütülmemesinden sonraki ifadeler için muhafaza edilmesi gerekmeyecektir. React gövdesi tarafından gerekli olan tüm değişkenlerin zaten bir kapanış olarak yakalandığına dikkat edin, bu yüzden gayet iyi çalışabilir.

Java aktör çerçevesi Kilim, aslında yığın bakımını, bir mesaj alan tepki üzerine açılan yığını kaydederek yapar.


Teşekkürler, bu çok bilgilendiriciydi. Ama +akod parçacıkları yerine kod parçacığını kastetmediniz +10mi?
jqno

Mükemmel cevap. Ben de anlamadım.
santiagobasulto


0

Scala / akka ile büyük bir çalışma yapmadım, ancak oyuncuların programlanma biçiminde çok önemli bir fark olduğunu anlıyorum. Akka, aktörlerin zaman dilimleme uygulaması olan akıllı bir threadpool ... Her seferinde dilim, Erlang'daki komut başına olabilen bir aktör tarafından tamamlanana kadar tek bir mesaj uygulaması olacak mı ?!

Bu, mevcut iş parçacığının diğer aktörleri planlama için diğer aktörleri dikkate almasını ima ettiğinden tepkinin daha iyi olduğunu düşünmeme neden oluyor.

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.