Kotlin / เนทีฟมัลติเธรดโดยใช้โครูทีน

Aug 19 2020

ฉันเคยถ่ายทำที่ kotlin หลายแพลตฟอร์มและมันยอดเยี่ยม แต่การทำเกลียวทำให้ฉันสะดุด การแช่แข็งของสถานะระหว่างเธรดมีความหมายในเชิงแนวคิดและใช้ได้ดีในตัวอย่างง่ายๆที่วัตถุขนาดเล็กหรือวัตถุดั้งเดิมถูกส่งผ่านไปมา แต่ในแอปพลิเคชันในโลกแห่งความเป็นจริงฉันไม่สามารถเข้าถึง InvalidMutabilityException ได้

ใช้ข้อมูลโค้ดทั่วไปต่อไปนี้จากแอป Android

class MainViewModel(
    private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
)

    private var coreroutineSupervisor = SupervisorJob()
    private var coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main + coreroutineSupervisor)

    private fun loadResults() {
        // Here: Show loading
        coroutineScope.launch {
            try {
                val result = withContext(Dispatchers.Default) { objectWhichContainsNetworking.fetchData() }
                // Here: Hide loading and show results
            } catch (e: Exception) {
                // Here: Hide loading and show error
            }
    }
}

ไม่มีอะไรซับซ้อนมาก แต่ถ้าใช้ในโค้ดทั่วไปและเรียกใช้จาก Kotlin / Native แล้ว InvalidMutabilityException บน MainViewModel

ดูเหมือนว่าเหตุผลนี้คืออะไรก็ตามที่ส่งผ่านด้วยContextจะถูกแช่แข็งซ้ำดังนั้นเนื่องจาก objectWhichContainsNetworking เป็นคุณสมบัติของ MainViewModel และใช้ร่วมกับContextจากนั้น MainViewModel จะถูกตรึง

คำถามของฉันคือนี่เป็นเพียงข้อ จำกัด ของโมเดลหน่วยความจำ Kotlin / Native ปัจจุบันหรือไม่? หรืออาจจะเป็น Coroutines รุ่นปัจจุบัน? และมีวิธีใดบ้างในรอบนี้?

หมายเหตุ: Coroutines เวอร์ชัน: 1.3.9-native-mt. kotlin เวอร์ชัน 1.4.0


แก้ไข 1: ดูเหมือนว่าโค้ดลดขนาดลงด้านบนใช้งานได้จริง ปรากฎว่ารหัสปรักปรำเป็นตัวแปรที่อัปเดตได้ในโมเดลมุมมอง (ใช้เพื่ออ้างอิงถึงสถานะมุมมองสุดท้าย) ซึ่งจะหยุดนิ่งจากนั้นจะโยนข้อยกเว้นเมื่อพยายามที่จะกลายพันธุ์ ฉันจะพยายามใช้ Flow / Channels เพื่อให้แน่ใจว่าไม่จำเป็นต้องมีการอ้างอิง var และดูว่าวิธีนี้ช่วยแก้ปัญหาโดยรวมได้หรือไม่

หมายเหตุ: หากมีวิธีหลีกเลี่ยงไม่ให้ MainViewModel ถูกแช่แข็งตั้งแต่แรกก็ยังคงยอดเยี่ยม!


แก้ไข 2: แทนที่ var ด้วย Flow ฉันไม่สามารถรับการรวบรวมขั้นตอนมาตรฐานใน iOS ได้จนกว่าจะใช้ตัวช่วยที่นี่:https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt.

MainViewModel ยังคงหยุดนิ่ง แต่เนื่องจากสถานะทั้งหมดไม่เปลี่ยนรูปจึงไม่ใช่ปัญหาอีกต่อไป หวังว่าจะช่วยใครสักคน!

คำตอบ

4 KevinGalligan Aug 19 2020 at 21:14

ในโค้ดดั้งเดิมของคุณคุณกำลังอ้างถึงฟิลด์ของอ็อบเจ็กต์พาเรนต์ซึ่งทำให้คุณจับภาพพาเรนต์ทั้งหมดและหยุดการทำงาน ไม่ใช่ปัญหาเกี่ยวกับโครูทีน Coroutines เป็นไปตามกฎเดียวกันกับไลบรารีการทำงานพร้อมกันอื่น ๆ ทั้งหมดใน Kotlin / Native มันค้างแลมด้าเมื่อคุณข้ามเธรด

class MainViewModel(
    private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
)

//yada yada
    private fun loadResults() {

        coroutineScope.launch {
            try {

                val result = withContext(Dispatchers.Default) {

                    //The reference to objectWhichContainsNetworking is a field ref and captures the whole view model
                    objectWhichContainsNetworking.fetchData() 

            }

        } catch (e: Exception) {}
    }
}

เพื่อป้องกันไม่ให้สิ่งนี้เกิดขึ้น:

class MainViewModel(
    private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
){
    init{
        ensureNeverFrozen()
    }
    //Etc

สิ่งที่ซับซ้อนที่สุดกับโมเดลหน่วยความจำคือสิ่งนี้ ทำความคุ้นเคยกับสิ่งที่ถูกจับและหลีกเลี่ยง มันไม่ใช่เรื่องยากเมื่อคุณคุ้นเคย แต่คุณต้องเรียนรู้พื้นฐาน

ฉันได้พูดถึงเรื่องนี้แล้ว:

Kotlin ในทางปฏิบัติ / ภาวะพร้อมกันของพื้นเมือง

Kotlin Native Concurrency Hands On

KotlinConf KN Concurrency

โมเดลหน่วยความจำกำลังเปลี่ยนไป แต่คงต้องใช้เวลาสักพักก่อนที่จะถึงที่หมาย เมื่อคุณคุ้นเคยกับโมเดลหน่วยความจำแล้วปัญหาที่ไม่เปลี่ยนรูปมักจะวินิจฉัยได้ง่าย