Kotlin yardımcı programları “daha ​​önce olur” garantileri veriyor mu?


10

Kotlin yardımcı programları "daha önce olur" garantisi veriyor mu?

Örneğin, mutableVarbu durumda başka bir konuya yazma ve daha sonra okuma (potansiyel olarak) arasında "daha önce olur" garantisi var mı :

suspend fun doSomething() {
    var mutableVar = 0
    withContext(Dispatchers.IO) {
        mutableVar = 1
    }
    System.out.println("value: $mutableVar")
}

Düzenle:

Belki ek örnek, daha fazla Kotlin-ish olmasına rağmen (değişebilirlik hariç) soruyu daha iyi açıklayacaktır. Bu kod iş parçacığı için güvenli mi?

suspend fun doSomething() {
    var data = withContext(Dispatchers.IO) {
        Data(1)
    }
    System.out.println("value: ${data.data}")
}

private data class Data(var data: Int)

JVM Kotlin üzerinde çalışırken Java ile aynı bellek modelini kullandığını unutmayın.
Lahana Salatası

1
@Slaw, bunu biliyorum. Ancak, kaputun altında çok fazla sihir var. Bu nedenle, koroutinlerden aldığım garantilerin olup olmadığını anlamak isterim, yoksa hepsi benim üzerimde.
Vasiliy

Herhangi bir şey varsa, ikinci örneğiniz daha da basit bir senaryo sunar: yalnızca içinde oluşturulan bir nesneyi kullanır withContext, ancak ilk örnek onu önce oluşturur, içinde mutasyona uğrar withContextve daha sonra da okur withContext. Dolayısıyla ilk örnek daha fazla iş parçacığı güvenlik özelliği kullanır.
Marko Topolnik

... ve her iki örnek de daha öncekilerin en önemlisi olan "program sırası" yönünü kullanır. Buradaki koroutin seviyesinden bahsediyorum, altta yatan JVM'den değil. Temel olarak, Kotlin coroutines'lerinin o kadar ciddi bir şekilde kırılıp kırılmadığını soruyorsunuz, daha önce program siparişi bile vermiyorlar.
Marko Topolnik

1
@ MarkoTopolnik, yanılıyorsam beni düzeltin, ama JLS aynı iş parçacığında yürütülmesi için yalnızca "program sırasını daha önce gerçekleşir" diye garanti eder. Şimdi, koroutinlerle, kod sıralı görünse de, pratikte onu farklı iş parçacıklarına yükleyen bazı makineler var. "Bu başka bir yorumdan) kontrol zamanımı bile boşa harcamayız" temel bir garanti olduğunu anlıyorum, ama bu soruyu titiz bir cevap almak istedi. Yazdığım örneklerin iş parçacığı açısından güvenli olduğundan eminim, ancak nedenini anlamak istiyorum.
Vasiliy

Yanıtlar:


6

Yazdığınız kodun paylaşılan duruma üç erişimi var:

var mutableVar = 0                        // access 1, init
withContext(Dispatchers.IO) {
    mutableVar = 1                        // access 2, write
}
System.out.println("value: $mutableVar")  // access 3, read

Üç erişim kesinlikle sıralı olarak düzenlenir, aralarında hiçbir eşzamanlılık yoktur ve Kotlin'in altyapısının iş parçacığı havuzuna teslim edilirken ve çağrı yapan koroutine geri dönerken önce bir kenar oluşturmaya özen gösterdiğinden emin olabilirsiniz IO.

Belki de daha ikna edici görünebilecek eşdeğer bir örnek:

launch(Dispatchers.Default) {
    var mutableVar = 0             // 1
    delay(1)
    mutableVar = 1                 // 2
    delay(1)
    println("value: $mutableVar")  // 3
}

Yana delaybir asılabilir fonksiyonudur ve biz kullanıyoruz çünkü Defaultbir iş parçacığı havuzu tarafından desteklenen hareket memurunu, çizgiler 1, 2 ve 3 her biri farklı bir iş parçacığı üzerinde çalıştırabilir. Bu nedenle daha önce olma ile ilgili sorunuz garantiden bu örnek için aynı derecede geçerlidir. Öte yandan, bu durumda, bu kodun davranışının sıralı yürütme ilkeleri ile tutarlı olduğu (umarım) tamamen açıktır.


1
Teşekkürler. Aslında "emin olun" dan sonra beni bu soruyu sormaya motive etti. Okuyabildiğim dokümanlar için herhangi bir bağlantı var mı? Alternatif olarak, bu kenarın oluşturulmasından önce gerçekleşen kaynak koduna bağlantılar da çok yardımcı olacaktır (birleştirme, senkronizasyon veya başka bir yöntem).
Vasiliy

1
Bu öyle temel bir garantidir ki, zamanımı kontrol ederken bile boşa harcamazdım. Kaputun altında kaynar executorService.submit()ve görevin tamamlanmasını bekleyen tipik bir mekanizma vardır (bir CompletableFutureveya benzer bir şeyi tamamlamak ). Kotlin coroutines'in bakış açısından burada hiçbir eşzamanlılık yoktur.
Marko Topolnik

1
Sorunuzu "İşletim sistemi bir iş parçacığı askıya alındıktan sonra başka bir çekirdeğe devam ederken daha önce gerçekleşecek mi garanti ediyor?" Sorusuna benzer bir şey olarak düşünebilirsiniz. İş parçacıkları, CPU çekirdeklerinin iş parçacığı için ne olduğunu koroutinlere yöneliktir.
Marko Topolnik

1
Açıklaman için teşekkürler. Ancak, bu sorunun neden işe yaradığını anlamasını istedim. Ne demek istediğini anlıyorum, ama şu ana kadar aradığım titiz cevap bu değil.
Vasiliy

2
Şey ... Aslında bu iş parçacığı kod sıralı olduğunu kurmuştur sanmıyorum. Elbette bunu iddia etti. Ben de performansı etkilemeden örneğin beklendiği gibi davrandığını garanti eden mekanizmayı görmek istiyorum.
G. Blake Meike

3

Kotlin'deki yardımcı programlar garantilerden önce gerçekleşir.

Kural şudur: Bir eşyordamın içinde kod öncesinde bir işlev çağrısı askıya almak önce olur kodundan sonra aramayı bekletin.

Koroutinleri normal iplikçiymiş gibi düşünmelisiniz:

Kotlin'deki bir program, birden çok iş parçacığında çalışabilse de, değişebilir durum açısından bir iş parçacığı gibidir. Aynı ortak programdaki hiçbir iki eylem eşzamanlı olamaz.

Kaynak: https://proandroiddev.com/what-is-concurrent-access-to-mutable-state-f386e5cb8292

Kod örneğine dönme. Lambda fonksiyon gövdelerinde çeşitlilik yakalamak, özellikle lambda koroutin olduğunda en iyi fikir değildir. Lambdadan önceki kod içerideki koddan önce olmaz.

Bkz. Https://youtrack.jetbrains.com/issue/KT-15514


Bir işlev çağrısı askıya kod önce: Kural aslında, bu olur daha önce kod işlevini askıya içeride olur daha önce kod çağrısını askıya sonra. Bu da "kodun program sırası aynı zamanda kodun önce gerçekleşen sırasıdır" şeklinde genelleştirilebilir . Bu ifadede askıya alınabilir işlevlere özgü herhangi bir şeyin olmadığına dikkat edin.
Marko Topolnik
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.