2013-03-02 9 views
12

askıda kalmasına neden oluyor. Rahatsız edici bir davranış farkettim. Döngü için kullanılan aralık olmayan paralel bir olduğundaScala: Nesne başlatıcısındaki paralel toplama, bir programın

object ParCollectionInInitializerTest { 
    def doSomething { println("Doing something") } 

    for (i <- (1 to 2).par) { 
    println("Inside loop: " + i) 
    doSomething 
    } 

    def main(args: Array[String]) { 
    } 
} 
programı, mükemmel masum ve

aşağıdaki çıkışı ile, uygun bir şekilde yerine getirmektedir: ı tek bir nesne aşağıdakilerden oluşan tek başına bir program varsayalım :

döngü içinde

: 1
Doing şey
döngü içinde: 2
şey

yaparak Paralel toplama kullanırken

yazık ki, programın sadece şimdiye doSomething yöntemi çağrılırken olmadan asılı, bu nedenle çıkış aşağıdaki gibidir: döngü içinde

: 2
döngü içinde: 1

Ve sonra program kilitleniyor.
Bu sadece kötü bir böcek mi? Scala-2.10 kullanıyorum.

+0

ile ilgili: http: // stackoverflow.com/questions/27549671/nasıl-teşhis-veya-tespit-deadlock-in-java-statik-başlatıcılar – Rich

cevap

15

Bu, yapım tamamlanmadan önce, tekil nesneye bir başvuru yayınlanırken Scala'da gerçekleşebilecek doğal bir sorundur. Tamamen oluşturulmadan önce ParCollectionInInitializerTest nesnesine erişmeye çalışan farklı bir iş parçacığı nedeniyle olur. main yöntemiyle bir ilgisi yoktur, bunun yerine, main yöntemini içeren nesneyi başlatmakla ilgili olması gerekir - bunu REPL içinde çalıştırmayı deneyin, ParCollectionInInitializerTest ifadesini kullanarak yazın ve aynı sonuçları elde edersiniz. Ayrıca, fork-join çalışan iş parçacığının daemon iş parçacığı ile ilgisi yoktur.

Tekil nesneler tembel olarak başlatılır. Her singleton nesnesi sadece bir kez başlatılabilir. Bu, nesneye erişen ilk iş parçacığının (sizin durumunuzda, ana iş parçacığı), nesnenin bir kilidini alması ve sonra onu başlatması gerektiği anlamına gelir. Daha sonra gelen her bir iş parçacığı, ana iş parçacığının nesneyi başlatmasını ve sonunda kilidi serbest bırakmasını beklemek zorundadır. Bu, singleton nesnelerinin Scala'da uygulanma şeklidir.

Sizin durumunuzda, paralel toplama çalışanı iş parçacığı, doSomething'u çağırmak için tekil nesnesine erişmeye çalışır, ancak ana iş parçacığı nesneyi başlatmayı tamamlayana kadar bunu yapamaz - böylece bekler. Diğer yandan, ana iş parçacığı, tüm çalışan iş parçacığı tamamlandığında, paralel işlem tamamlanana kadar kurucuda bekler - ana iş parçacığı, her zaman tekil için başlatma kilidini tutar. Bu nedenle, bir kilitlenme oluşur.

Aşağıda gösterildiği gibi, ya da sadece parçacığı ile 2.10'dan geleceklerine bu davranışa neden olabilir

:

scala> ParCollection 

ve Çoğaltma:

def execute(body: =>Unit) { 
    val t = new Thread() { 
    override def run() { 
     body 
    } 
    } 

    t.start() 
    t.join() 
} 

object ParCollection { 

    def doSomething() { println("Doing something") } 

    execute { 
    doSomething() 
    } 

} 

yazmak sonra Repl yapıştırın ve asar.

+2

Mükemmel açıklama, teşekkürler! –

+1

Eşzamanlı engelleme yürütme ve tembel başlatma, birlikte güzel oynamamaktadır. Bu, Scala'da (ve bu konu için Java) daha genel bir sorundur. Bu SIP'ye bakın: http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html – axel22

+0

İmdat olarak değil, bunun geliştiriciler için bir tuzak olduğunu düşünüyorum. İlk cevap ve yorumlarınız için çok müteşekkirim. Bu arada cevabın bana karşı açık olduğunu düşünüyorum. – matanster