android, coroutine,

Shared mutable state & concurreny

Ella Ella Follow Dec 27, 2021 ยท 6 mins read
Shared mutable state & concurreny
Share this

๐Ÿค–

๊ฐœ๋… 8๏ธโƒฃ Shared mutable state and concurreny

์ฝ”๋ฃจํ‹ด์€ Dispatchers.Default์™€ ๊ฐ™์€ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ Dispatcher๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰๋œ๋‹ค. ์ด๋Ÿฌํ•œ ์ฝ”๋ฃจํ‹ด์—์„œ ๋ณ‘๋ ฌ๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผํ•  ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š”๋ฐ, ๋ฐ”๋กœ shared mutable state์— ์ ‘๊ทผํ•  ๋•Œ์˜ synchronization ๋ฌธ์ œ์ด๋‹ค.

Problem ๐Ÿ‘ฟ

์ˆ˜์ฒœ๋ฒˆ ๊ฐ™์€ ํ–‰๋™์„ ๋ฐ˜๋ณตํ•˜๋Š” ๋ฐฑ๊ฐœ์˜ ์ฝ”๋ฃจํ‹ด์„ launch ํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž.

suspend fun massiveRun(action: suspend () -> Unit) {
    val n = 100  // number of coroutines to launch
    val k = 1000 // times an action is repeated by each coroutine
    val time = measureTimeMillis {
        coroutineScope { // scope for coroutines
            repeat(n) {
                launch {
                    repeat(k) { action() }
                }
            }
        }
    }
    println("Completed ${n * k} actions in $time ms")
}

์ด๋Ÿฌํ•œ suspend ํ•จ์ˆ˜์— counter๋ผ๋Š” shared mutable variable๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ๊ฐ„๋‹จํ•œ action์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„ฃ์–ด์„œ ๋Œ๋ ค๋ณด์ž. ์—ฌ๊ธฐ์„œ๋Š” Dispatcher๋กœ Dispatcher.Default๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

var counter = 0

fun main() = runBlocking {
    withContext(Dispatchers.Default) {
        massiveRun {
            counter++
        }
    }
    println("Counter = $counter")
}

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋Œ๋ ธ์„ ๋•Œ ์–ด๋–ค ๊ฐ’์ด ์ถœ๋ ฅ๋ ๊นŒ??๐Ÿ˜ฏ ๋‹จ์ˆœํ•˜๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฐ’๋“ค์ด ์ถœ๋ ฅ๋  ๊ฒƒ ๊ฐ™๋‹ค.

// ๋‹จ์ˆœํžˆ ์ƒ๊ฐํ–ˆ์„ ๋•Œ์˜ ์ถœ๋ ฅ๊ฐ’
Completed 100000 actions in 23 ms
Counter = 100000

ํ•˜์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” Counter์˜ ๊ฐ’์ด ์œ„์™€๋Š” ์ „ํ˜€ ๋‹ค๋ฅธ ๊ฐ’์ด ๋‚˜์˜ฌ ๊ฒƒ์ด๋‹ค!! ์™œ๋ƒํ•˜๋ฉด 100๊ฐœ์˜ ์ฝ”๋ฃจํ‹ด์ด counter๋ผ๋Š” ๋ณ€์ˆ˜๋ฅผ synchronization ํ•˜์ง€ ์•Š๊ณ  ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ๋กœ ๋™์‹œ์— ์ฆ๊ฐ€์‹œ์ผฐ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๋”ฐ๋ผ์„œ ์‹ค์ œ ์ถœ๋ ฅ๊ฐ’์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.

Completed 100000 actions in 23 ms
Counter = 85519

โžก๏ธ Volatiles๋กœ๋Š” ํ•ด๊ฒฐ์ด ์•ˆ๋ ๊นŒ..?๐Ÿค”

๊ทธ๋ ‡๋‹ค๋ฉด ํ˜น์‹œ volatile ๋ณ€์ˆ˜๊ฐ€ concurrency ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ์ง€ ์•Š์„๊นŒ..? ์œ„์˜ ์ฝ”๋“œ์˜ counter ๋ณ€์ˆ˜์— Volatile ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์„œ ๋‹ค์‹œ ์ฝ”๋“œ๋ฅผ ๋Œ๋ ค๋ณด์ž!!

@Volatile // in Kotlin `volatile` is an annotation 
var counter = 0

fun main() = runBlocking {
    withContext(Dispatchers.Default) {
        massiveRun {
            counter++
        }
    }
    println("Counter = $counter")
}

์ฝ”๋“œ๊ฐ€ ์•ž์ „๋ณด๋‹ค ๋” ๋Š๋ฆฌ๊ฒŒ ๋Œ์•„๊ฐ€๊ธฐ๋Š” ํ•˜์ง€๋งŒ, ์—ฌ์ „ํžˆ Counter๊ฐ€ 100000์œผ๋กœ ์ฐํžˆ์ง€๋Š” ์•Š๋Š”๋‹ค. ์™œ๋ƒํ•˜๋ฉด volatile ๋ณ€์ˆ˜๊ฐ€ ํ•ด๋‹น ๋ณ€์ˆ˜์— linearizable(=atomic)ํ•œ read & write๋ฅผ ๋ณด์žฅํ•ด์ฃผ๊ธด ํ•˜์ง€๋งŒ, ๋” ํฐ action๋“ค์—๊ฒŒ atomocity๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๐ŸŒŸ Thread-safeํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๋“ค

thread-safeํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด (1)์Šค๋ ˆ๋“œ์™€ (2)์ฝ”๋ฃจํ‹ด์˜ ํ•ด๊ฒฐ์ฑ…์ด ๋  ์ˆ˜ ์žˆ๋‹ค. thread-safeํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๋Š” shared state๋ฅผ ์ด์šฉํ•˜๋Š” ์—ฐ์‚ฐ์„ ์œ„ํ•œ synchronization ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ฃผ๋ฉฐ, synchronized, linearizable, atomic์ด ์ด์— ํ•ด๋‹นํ•œ๋‹ค.

1๏ธโƒฃ AtomicInteger

๊ฐ„๋‹จํ•œ counter์ผ ๊ฒฝ์šฐ์—๋Š” AtomicInteger๋ฅผ ์“ธ ์ˆ˜ ์žˆ์œผ๋ฉฐ, AtomicInteger๋Š” incrementAndGet() ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ง„๋‹ค.

val counter = AtomicInteger()

fun main() = runBlocking {
    withContext(Dispatchers.Default) {
        massiveRun {
            counter.incrementAndGet()
        }
    }
    println("Counter = $counter")
}
Completed 100000 actions in 26 ms
Counter = 100000

AmomicInteger๋ฅผ ์“ฐ๋ฉด ์œ„์—์„œ Volatile ๋ณ€์ˆ˜๋ฅผ ์ผ์„ ๋•Œ์™€๋Š” ๋‹ฌ๋ฆฌ counter๊ฐ€ 100000๊ฐ€ ์ •ํ™•ํžˆ ์ฐํžˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Œœ. AtomicInteger๋Š” ๋ณดํ†ต์˜ counter์™€ collection, queue ๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ ํ‘œ์ค€ ์ž๋ฃŒ๊ตฌ์กฐ๋‚˜ ๊ธฐ๋ณธ ์—ฐ์‚ฐ์—์„œ ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ค€๋‹ค.
๊ทธ๋Ÿฌ๋‚˜, thread-safe๋ฅผ ์œ„ํ•œ implementation์ด ๋˜์ง€์•Š์€ ๋ณต์žกํ•œ state๋‚˜ ๋ณต์žกํ•œ ์—ฐ์‚ฐ์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒƒ์€ ์–ด๋ ค์šด ์ผ์ด๋‹ค.

2๏ธโƒฃ Thread confinement fine-grained

Thread confinement, ์ฆ‰ ์Šค๋ ˆ๋“œ๋ฅผ ์ œํ•œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์“ฐ๋ฉด shared mutable state์˜ ๋™๊ธฐํ™” ๋ฌธ์ œ์— ๋˜๋‹ค๋ฅธ ํ•ด๊ฒฐ์ฑ…์ด ๋  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ํŠน์ • shared state๋กœ ์ ‘๊ทผํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ๋“ค์ด ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋ฅผ ์ด์šฉํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ฃผ๋กœ UI ์–ดํ”Œ๋ฆฌ์บ์ด์…˜์—์„œ ์‚ฌ์šฉ๋˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ๋ชจ๋“  UI state๋Š” single event-dispatch/application ์Šค๋ ˆ๋“œ๋กœ ์ œํ•œ๋œ๋‹ค. ์ฝ”๋ฃจํ‹ด์—์„œ single-thread context ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

val counterContext = newSingleThreadContext("CounterContext")
var counter = 0

fun main() = runBlocking {
    withContext(Dispatchers.Default) {
        massiveRun {
            // confine each increment to a single-threaded context
            withContext(counterContext) {
                counter++
            }
        }
    }
    println("Counter = $counter")
}
Completed 100000 actions in 2190 ms
Counter = 100000

์œ„์˜ ์ฝ”๋“œ๋Š” ๊ฐ๊ฐ์˜ increment๋ฅผ single-thread๋กœ ์ œํ•œํ•˜๋Š” fine-grained thread-confinement๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ๋Š๋ฆฌ๊ฒŒ ๋™์ž‘ํ•œ๋‹ค. ๊ฐ๊ฐ์˜ ++์€ withContext ๋ธ”๋ก์„ ์ด์šฉํ•˜์—ฌ multi-threaded Dispatchers.Default ์ปจํ…์ŠคํŠธ์—์„œ single-threaded ์ปจํ…์ŠคํŠธ๋กœ ์ „ํ™˜์‹œํ‚จ๋‹ค.

3๏ธโƒฃ Thread confinement coarse-grained

thread-confinement๋Š” ์˜ˆ๋ฅผ ๋“ค์–ด์„œ, state๋ฅผ ์—…๋ฐ์ดํŠธ ์‹œํ‚ค๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ์ œํ•œ์‹œํ‚ค๋Š” ๊ฒฝ์šฐ์™€ ๊ฐ™์ด ํฐ ๋ฉ์–ด๋ฆฌ์—์„œ ์‚ฌ์šฉ๋œ๋‹ค. ์•„๋ž˜์˜ ์˜ˆ์ œ์—์„œ๋Š” single thread ์ปจํ…์ŠคํŠธ์—์„œ ๊ฐ ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•˜๋ฉด์„œ ์‹œ์ž‘ํ•œ๋‹ค.

val counterContext = newSingleThreadContext("CounterContext")
var counter = 0

fun main() = runBlocking {
    // confine everything to a single-threaded context
    withContext(counterContext) {
        massiveRun {
            counter++
        }
    }
    println("Counter = $counter")
}

์œ„์˜ ์ฝ”๋“œ๋Š” ์•ž์—์„œ์˜ increment๋งˆ๋‹ค single-thread๋กœ ์ œํ•œํ•˜๋Š” fineํ•œ ๊ฒฝ์šฐ์™€๋Š” ๋‹ฌ๋ฆฌ, coarseํ•˜๊ฒŒ massiveRun ๋ญ‰ํ……์ด๋ฅผ single-thread๋กœ ์ œํ•œํ•จ์œผ๋กœ์จ ๋” ๋น ๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๊ณ  ์ •ํ™•ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋‚ด๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

4๏ธโƒฃ Mutual exclusion

์ƒํ˜ธ ๋ฐฐ์ œ๋Š” ๋™์‹œ์— ์ ‘๊ทผํ•ด์„œ๋Š” ์ ˆ๋Œ€๋กœ ์•ˆ๋˜๋Š” ์ž„๊ณ„์˜์—ญ์—์„œ shared state๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ ์“ธ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. blocking์„ ํ•  ๋•Œ ๊ฐœ๋ฐœ์ž๋“ค์€ ๋ณดํ†ต synchronized๋‚˜ ReentrantLock๋ฅผ ์ด์šฉํ•œ๋‹ค. ์ฝ”๋ฃจํ‹ด์˜ ๋‹ค๋ฅธ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ๋Š” Mutex๊ฐ€ ์žˆ๋‹ค. Mutex๋Š” ์ž„๊ณ„์˜์—ญ์˜ ์ƒํƒœ๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ lock ๋ฉ”์†Œ๋“œ์™€ unlock ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. Mutex.lock()๋ฉ”์†Œ๋“œ๋Š” suspending ํ•จ์ˆ˜์ด๋ฉฐ thread๋ฅผ blockํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ํŠน์ง•์ด ์žˆ๋‹ค.
withLock์ด๋ผ๋Š” ํ™•์žฅํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด mutex.lock(); try { ... } finally { mutex.unlock() } ํŒจํ„ด์„ ๊ฐ„๋‹จํžˆ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

val mutex = Mutex()
var counter = 0

fun main() = runBlocking {
    withContext(Dispatchers.Default) {
        massiveRun {
            // protect each increment with lock
            mutex.withLock {
                counter++
            }
        }
    }
    println("Counter = $counter")
}

์œ„์˜ ์˜ˆ์ œ๋Š” fine-grainedํ•œ locking ์˜ˆ์ œ๋ผ์„œ ๋น„์šฉ์ด ๋งŽ์ด ๋“ ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ์ฃผ๊ธฐ์ ์œผ๋กœ shared state๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜์ง€๋งŒ ํ•ด๋‹น state๋ฅผ confine์‹œํ‚ฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ธฐ์กด์˜ ์Šค๋ ˆ๋“œ ํ’€์— ๋งˆ๋•…ํžˆ ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ์ข‹์€ ํ•ด๊ฒฐ์ฑ…์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

5๏ธโƒฃ Actors

actor๋Š” state(์ฝ”๋ฃจํ‹ด์— confine๋˜๊ณ  ์บก์Šํ™”๋˜๋Š”)์™€ channel(๋‹ค๋ฅธ ์ฝ”๋ฃจํ‹ด๋“ค๊ณผ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ํ•˜๋Š”)๋กœ ์ด๋ฃจ์–ด์ง„ ์ฝ”๋ฃจํ‹ด entity์ด๋‹ค. ๊ฐ„๋‹จํ•œ actor๋Š” ๊ทธ๋ƒฅ ํ•จ์ˆ˜๋กœ ์“ธ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ณต์žกํ•œ state๋ฅผ ๊ฐ€์ง„ actor๋Š” ํด๋ž˜์Šค๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋” ์ ํ•ฉํ•˜๋‹ค.
actor์˜ ์ฝ”๋ฃจํ‹ด ๋นŒ๋”๋Š” ์•กํ„ฐ์— ๋Œ€ํ•œ ๋‹จ์ผ ์ฐธ์กฐ ํ•ธ๋“ค๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ (1) ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•˜๊ธฐ ์œ„ํ•ด์„œ actor์˜ mailbox channel์„ scope์— ๊ฒฐํ•ฉํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  (2) ์ „์†ก ์ฑ„๋„์„ ๊ฒฐ๊ณผ job object์— ๊ฒฐํ•ฉํ•œ๋‹ค. actor๋ฅผ ์“ฐ๊ธฐ ์œ„ํ•ด ์ฒซ ๋ฒˆ์งธ๋กœ ํ•  ์ผ์€ actor๊ฐ€ ์ฒ˜๋ฆฌํ•  ๋ฉ”์‹œ์ง€ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ฝ”ํ‹€๋ฆฐ์˜ sealed class๊ฐ€ ์ด๋Ÿฌํ•œ ๋ฉ”์‹œ์ง€ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•  ๋•Œ ์ ํ•ฉํ•˜๋‹ค. ๋ฐ‘์˜ ์˜ˆ์ œ์—์„œ๋Š” CounterMsg sealed class๋ฅผ ์ด์šฉํ•˜์—ฌ IncCounter ๋ฉ”์‹œ์ง€์™€ GetCounter ๋ฉ”์‹œ์ง€๋ฅผ ์ •์˜ํ•˜์˜€๋‹ค. IncCountr ๋ฉ”์‹œ์ง€๋Š” counter๋ฅผ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค. ๊ทธ๋ฆฌ๊ณ  GetCounter๋Š” ๊ฐ’์„ getํ•ด์ค€๋‹ค. GetCounter๋Š” response๋ฅผ ๋ณด๋‚ด์•ผ ํ•˜๋ฏ€๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋‹ฌ๋ ค์žˆ๋‹ค. CompletedDeffered(single value๋ฅผ ๋‚˜ํƒ€๋ƒ„)๊ฐ€ response๋กœ ์‚ฌ์šฉ๋˜๊ฒŒ ๋œ๋‹ค.

// Message types for counterActor
sealed class CounterMsg
object IncCounter : CounterMsg() // one-way message to increment counter
class GetCounter(val response: CompletableDeferred<Int>) : CounterMsg() // a request with reply

๊ทธ๋Ÿฌ๊ณ  ๋‚˜์„œ, actor ์ฝ”๋ฃจํ‹ด ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ actor๋ฅผ launchํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•œ๋‹ค.

// This function launches a new counter actor
fun CoroutineScope.counterActor() = actor<CounterMsg> {
    var counter = 0 // actor state
    for (msg in channel) { // iterate over incoming messages
        when (msg) {
            is IncCounter -> counter++
            is GetCounter -> msg.response.complete(counter)
        }
    }
}

์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฉ”์ธ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž.

fun main() = runBlocking<Unit> {
    val counter = counterActor() // create the actor
    withContext(Dispatchers.Default) {
        massiveRun {
            counter.send(IncCounter)
        }
    }
    // send a message to get a counter value from an actor
    val response = CompletableDeferred<Int>()
    counter.send(GetCounter(response))
    println("Counter = ${response.await()}")
    counter.close() // shutdown the actor
}
Completed 100000 actions in 1016 ms
Counter = 100000

์–ด๋–ค context์—์„œ acter๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š๋ƒ๋Š” ์ค‘์š”ํ•˜์ง€ ์•Š๋‹ค. actor ์ž์ฒด๊ฐ€ ์ฝ”๋ฃจํ‹ด์ด๊ณ , ์ฝ”๋ฃจํ‹ด์€ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜์–ด์„œ ํŠน์ • ์ฝ”๋ฃจํ‹ด์œผ๋กœ state๋ฅผ ํ•œ์ •ํ•˜๋Š” ๊ฒƒ์€ shared mutable state์˜ ๋ฌธ์ œ์ ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์ด ๋  ์ˆ˜ ์žˆ๋‹ค. actor๋Š” private state๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚ฌ ์ง€๋„ ๋ชจ๋ฅด์ง€๋งŒ, lockํ•  ํ•„์š” ์—†์ด message๋ฅผ ํ†ตํ•ด์„œ ์„œ๋กœ ์˜ํ–ฅ์„ ๋ผ์น  ๋ฟ์ด๋‹ค.
์œ„์˜ ์ƒํ™ฉ์—์„œ๋Š” Actor๊ฐ€ ํ•ญ์ƒ ์ž‘์—…ํ•  ๊ฒƒ์ด ์žˆ๊ณ  ๋‹ค๋ฅธ context๋กœ ์ „ํ™˜์‹œํ‚ฌ ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์—, Actor๊ฐ€ load ํ•˜์— locking์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•์ด๋‹ค.

[์ฐธ๊ณ  ์‚ฌ์ดํŠธ]
Kotlin ๊ณต์‹ ๋ฌธ์„œ - Shared mutable state and concurrency

Join Newsletter
Get the latest news right in your inbox. We never spam!
Ella
Written by Ella Follow
Android Developer, love to explore new ideas and write on my morning coffee!