Middle+
როგორ შეიძლება Kotlin- ში სინქრონიზაცია განხორციელდეს კორუტინების გამოყენებით?
sobes.tech AI
პასუხი AI-სგან
კოტლინ კორუტინებში სინქრონიზაცია აღწევს სპეციალური primitive-ებისა და მონაცემთა სტრუქტურების საშუალებით kotlinx.coroutines ბიბლიოთეკიდან.
ძირითადი მიდგომები:
-
Mutex
- უზრუნველყოფს ურთიერთგამორიცხულობას კრიტიკული სექციების დაცვისთვის.
- კორუტინა, რომელიც ცდილობს მიიღოს Mutex-ს, ბლოკდება მანამდე, სანამ ის თავისუფალი არ გახდება.
- აქვს
lock()დაunlock()ფუნქციები, ასევე უფრო იდეომატურიwithLock.
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.* val mutex = Mutex() var counter = 0 suspend fun increment() { mutex.withLock { counter++ } } suspend fun main() = coroutineScope { repeat(10_000) { launch { increment() } } println(counter) // ველით 10000 } -
Semaphore
- შეზღუდავს ერთდროულად მოქმედ კორუტინების რაოდენობას, რომლებიც შეიძლება ერთდროულად მიაღწიონ რესურსს ან შეასრულონ გარკვეული ბლოკი.
- მართავს ნებართვების პულს.
- აქვს
acquire()დაrelease()ფუნქციები, ასევეwithPermit.
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.* val semaphore = Semaphore(2) // ერთდროულად მუშაობს 2 კორუტინა suspend fun doLimitedWork(id: Int) { semaphore.withPermit { println("კორუტინა $id ნებართვას იღებს. მუშაობა...") delay(100) // მუშაობის იმიტაცია println("კორუტინა $id ნებართვას ტოვებს.") } } suspend fun main() = coroutineScope { repeat(5) { i -> launch { doLimitedWork(i) } } } -
ატომური ოპერაციები (
kotlinx.coroutines.atomic-დან)- უზრუნველყოფს ნაკადების უსაფრთხო ოპერაციებს primitives და ლინკებზე.
- იყენებს დაბალ დონეზე CPU-ის ინსტრუქციებს (CAS - Compare-and-Swap).
- შესაფერისია მარტივი ოპერაციებისათვის, სადაც არ არის საჭირო გამოხატული ბლოკირება.
import kotlinx.coroutines.atomic.AtomicInt import kotlinx.coroutines.* val atomicCounter = AtomicInt(0) suspend fun atomicIncrement() { atomicCounter.incrementAndGet() } suspend fun main() = coroutineScope { repeat(10_000) { launch { atomicIncrement() } } println(atomicCounter.value) // ველით 10000 } -
გაერთიანებული მონაცემთა სტრუქტურები ერთ-თრედის დისპეტჩერით
- ყველაზე მარტივი მიდგომა.
Dispatchers.Default.limitedParallelism(1)ანnewSingleThreadContextგამოყენებით, კორუტინების გაშვება ერთ-თრედის კონტექსტში უზრუნველყოფს კოდის სერიულ შესრულებას, რაც თავიდან აცილებს მონაცემთა შეჯახებას. ეს არ არის გამოხატული სინქრონიზაციის primitive, მაგრამ უზრუნველყოფს სერილიზაციას.
import kotlinx.coroutines.* val mySingleThreadContext = newSingleThreadContext("SingleThread") var sharedData = mutableListOf<Int>() suspend fun addToSharedData(value: Int) { sharedData.add(value) // უსაფრთხოა, რადგან ერთ თრედში ხდება } suspend fun main() = withContext(mySingleThreadContext) { repeat(10_000) { launch { addToSharedData(it) } } println(sharedData.size) // ველით 10000 } - ყველაზე მარტივი მიდგომა.
-
ჩანანელები (Channels)
- არ არის სინქრონიზაციის primitive, მაგრამ შეიძლება გამოყენებულ იქნას მონაცემების უსაფრთხო გადაცემისთვის კორუტინებს შორის, რაც ირიბად აგვარებს მონაცემთა წვდომის პრობლემებს.
- მონაცემთა ნაკადის გადაცემის საშუალებაა ერთი კორუტინიდან მეორე კორუტინამდე.
მოდელი დამოკიდებულია გამოყენების სცენარზე. Mutex და Semaphore უზრუნველყოფენ კლასიკურ ბლოკირების მექანიზმებს, Atomic ოპერაციები ეფექტურია მარტივი ატომური განახლებებისთვის, ხოლო ერთ-თრედის დისპეტჩერი სასარგებლოა, როდესაც საერთო მონაცემების წვდომა უნდა იყოს მკაცრად სერიული. Channels გამოიყენება კომუნიკაციის და კოორდინაციისთვის, არა პირდაპირი საერთო რესურსების დაცვისთვის.