Middle+
Ինչպես կարելի է իրականացնել սինխրոնիզացիան Kotlin- ում՝ օգտագործելով կորուտիններ?
sobes.tech AI
Պատասխան AI-ից
Կոնկուրսիաների սինխրոնիզացիան Kotlin- ի կորգինների մեջ հասնում է հատուկ 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 } -
Ընթերցող-գրող տվյալների կառուցվածքներ մեկ-թերթային dispatcher-ով
- Ամենահեշտ մոտեցումը։ Կորգինների գործարկումը մեկ-թերթային կոնտեքստում (
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
- Չեն հանդիսանում սինխրոնիզացիայի primitives, բայց կարող են օգտագործվել տվյալների անվտանգ փոխանցման համար կորգինների միջև, ինչը անուղղակիորեն լուծում է մուտքի սինխրոնիզացիայի խնդիրները։
- Առաջարկում են տվյալների հոսքի փոխանցում մեկ կորգինից մյուսին։
Ընտրությունը կախված է օգտագործման սցենարից։ Mutex և Semaphore տրամադրում են դասական բլոկավորման մեխանիզմներ, Atomic-ները արդյունավետ են պարզ ատոմարային թարմացումների համար, իսկ մեկ-թերթային dispatcher- ը հարմար է, երբ ընդհանուր տվյալների մուտքը պետք է լինի խիստ հերթական։ Channels- ը օգտագործվում է հաղորդակցման և համակարգման համար, ոչ թե ուղղակիորեն ընդհանուր ռեսուրսների պաշտպանության համար։