import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class UserDownloader(
    private val api: NetworkService
) {
    private val users = mutableListOf<User>()

    fun downloaded(): List<User> = users.toList()

    suspend fun fetchUser(id: Int) {
        val newUser = api.fetchUser(id)
        users.add(newUser)
    }
}

class NetworkService {
    suspend fun fetchUser(id: Int) : User {
        delay(2)
        return User("User$id")
    }
}

class User(val id: String)

suspend fun main() {
    val downloader = UserDownloader(NetworkService())
    coroutineScope {
        repeat(1_000_000) {
            launch {
                downloader.fetchUser(it)
            }
        }
    }

    println(downloader.downloaded().size) // 816051
}
fun main() = runBlocking {
    var count = 0

    massiveRun {
        count++
    }

    println(count) // 10_000_000 보다 작은 숫자
}

suspend fun massiveRun(action: suspend () -> Unit) {
    withContext(Dispatchers.Default) {
        repeat(10_000_000) {
            launch {
                action()
            }
        }
    }
}

동기화 블로킹

  1. synchronized block
fun main() = runBlocking {
    var count = 0

    val lock = Any()

    massiveRun {
        synchronized(lock) {
            count++
        }
    }

    println(count) // 10000000
}

→ 스레드 블로킹(자원 낭비) 없이 중단하거나 충돌을 회피할 수 있는 방법을 사용해야 한다.

  1. 원자성
fun main() = runBlocking {
    var counter = AtomicInteger()

    massiveRun {
        counter.incrementAndGet()
    }

    println(counter.get()) // 10000000
}
  1. 싱글스레드로 제한된 디스패처
fun main() = runBlocking {
    var counter = 0
    val dispatcher = Dispatchers.IO.limitedParallelism(1)

    massiveRun {
        withContext(dispatcher) {
            counter++
        }
    }

    println(counter) // 10000000
}