Sobes.tech
Middle+

Γιατί δεν συνιστάται η χρήση μπλοκ συγχρονισμού και παρόμοιων δομών κατά την εργασία με κορουτίνες;

sobes.tech AI

Απάντηση από AI

Η χρήση μπλοκαριστικών κατασκευών, όπως το synchronized ή το lock από τη βασική βιβλιοθήκη, κατά την εργασία με κορουτίνες οδηγεί σε αναποτελεσματική χρήση πόρων και μπορεί να προκαλέσει προβλήματα.

Κύριοι λόγοι:

  • Μπλοκάρισμα νήματος: Τα μπλοκ συγχρονισμού σταματούν την εκτέλεση του νήματος, όχι μόνο των κορουτίνων. Αν μια λειτουργία Suspending εκτελείται μέσα σε ένα μπλοκ συγχρονισμού, το νήμα θα μπλοκαριστεί μέχρι να ολοκληρωθεί η λειτουργία Suspending και να απελευθερωθεί το κλείδωμα. Αυτό μπορεί να οδηγήσει σε "πείνα" άλλων κορουτίνων που χρησιμοποιούν το ίδιο νήμα.
  • Ασυμβατότητα με λειτουργίες Suspending: Ο μηχανισμός εργασίας των κορουτίνων (μεταφορά πλαισίου, συνεχίσεις) δεν λαμβάνει υπόψη τα μπλοκαρίσματα σε επίπεδο νήματος. Η εκτέλεση μιας λειτουργίας Suspending μέσα σε ένα μπλοκ συγχρονισμού παραβιάζει τη μη μπλοκαριστική φύση των κορουτίνων.
  • Παραβίαση των αρχών δομημένου ανταγωνισμού: Οι κορουτίνες έχουν το δικό τους σύστημα σημάνσεων ακύρωσης και ιεραρχίας. Τα μπλοκαρίσματα σε επίπεδο νήματος αγνοούν αυτό το σύστημα, καθιστώντας δύσκολη τη σωστή διαχείριση ακυρώσεων και τον έλεγχο κύκλου ζωής.
  • Κίνδυνος deadlocks: Ο συνδυασμός μπλοκαρισμάτων νήματος και μηχανισμών ανταγωνισμού κορουτίνων αυξάνει την πιθανότητα πολύπλοκων deadlocks, που είναι δύσκολα στην αποσφαλμάτωση.

Αντί για μπλοκαρίσματα σε επίπεδο νήματος για συγχρονισμό πρόσβασης σε κοινόχρηστους πόρους σε κορουτίνες, θα πρέπει να χρησιμοποιούνται εξειδικευμένα primitives ανταγωνισμού από τη βιβλιοθήκη kotlinx.coroutines:

  • Mutex: Παρέχει αμοιβαία αποκλεισμό. Η κορουτίνα που προσπαθεί να αποκτήσει ένα καταληφθέν mutex αναστέλλεται (χωρίς να μπλοκάρει το νήμα) μέχρι να απελευθερωθεί το mutex.
  • Semaphore: Περιορίζει τον αριθμό των κορουτίνων που έχουν πρόσβαση σε πόρο ταυτόχρονα.
  • Channel: Χρησιμοποιείται για ανταλλαγή δεδομένων μεταξύ κορουτίνων.
  • Ατομικές μεταβλητές: Για απλές ατομικές λειτουργίες.

Παράδειγμα χρήσης Mutex:

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.*

val mutex = Mutex()
var counter = 0

suspend fun incrementCounter() {
    mutex.withLock {
        // Κρίσιμη ενότητα
        counter++
    }
}

fun main() = runBlocking {
    // Δημιουργία πολλών κορουτίνων για ταυτόχρονη πρόσβαση
    repeat(1000) {
        launch {
            incrementCounter()
        }
    }
    delay(1000) // Δίνουμε χρόνο στις κορουτίνες να εκτελεστούν
    println("Counter: $counter") // Αναμένεται 1000
}

Το mutex.withLock είναι μια suspending-λειτουργία. Αν το mutex είναι κατειλημμένο, η κορουτίνα που καλεί το withLock αναστέλλεται και απελευθερώνει το νήμα για άλλες εργασίες. Όταν το mutex γίνει διαθέσιμο, η κορουτίνα συνεχίζει. Αυτό δεν μπλοκάρει το νήμα, σε αντίθεση με το synchronized.