fun getProfile(id : Int) : Profile {
val basicUserInfo = getUserInfo(id)
val contactInfo = getContactInfo(id)
return createProfile(basicUserInfo, contactInfo)
} // End of getProfile()
순차 코드의 문제
동시성 코드에 비해 성능이 저하될 수 있다.
코드가 실행되는 하드웨어를 제대로 활용하지 못할 수 있음
위 코드 에서 getUserInfo()
, getContactInfo()
가 각 1초가 소요된다고 하면 getProfile()
는 2초 이상의 시간이 필요하다.
여기서 getUserInfo()
, getContactInfo()
를 동시에 호출하면 절반의 시간으로 줄일 수 있다.
suspend fun getProfile(id : Int) {
val basicUserInfo = asyncGetUserInfo(id)
val contactInfo = asyncGetContactInfo(id)
createProfile(basicUserInfo.await(), contactInfo.await())
} // End of getProfile()
해당 코드에서는 asyncGetUserInfo()
가 asyncGetContactInfo()
보다 먼저 수행이 완료될지 알 수 없다.
실제로 이전의 순차적인 버전보다 2배 빠르게 수행될 수 있지만 실행할 때 가변성이 발생한다.
그래서 createProfile()
내부에 await()
호출이 있는 이유이다.
이것이 하는 일은 asyncGetUserInfo()
와 asyncGetContactInfo()
가 모두 완료될 때 까지 getProfile()
의 실행을 일시 중단하는 것이다.
둘 다 완료됐을 때만 createProfile()
이 실행된다. 어떤 동시성 호출이 먼저 종료되든지에 관계없이 getProfile()
의 결과가 항상 같은 결과가 나옴을 보장한다.
요약하자면
⭐ 동시성은 2개 이상의 실행 시간이 겹칠 때 발생한다.
⭐ 중첩이 발생하려면 2개 이상의 실행 쓰레드가 필요하다. 이런 쓰레드 들이 단일 코어에서 실행되면 병렬이 아니라 동시에 실행되는데, 단일 코어가 서로 다른 쓰레드의 인스트럭션으 교차 배치해서, 쓰레드들의 실행을 효율적으로 겹쳐서 실행한다.
⭐ 병렬은 2개의 알고리즘이 정확히 같은 시점에 실행될 때 발생한다.
⭐ 이것이 가능하려면 2개 이상의 코어와 2개 이상의 쓰레드가 있어야 각 코어가 동시에 쓰레드의 인스트럭션을 실행할 수 있다. 병렬은 동시성을 의미하지만 동시성은 병렬성이 없이도 발생할 수 있다.
원자성 위반
원자성 작업이란 작업이 사용하는 데이터를 간섭없이 접근할 수 있음을 의미한다.
단일 쓰레드에서는 모든 코드가 순차적으로 동작하기 때문에 모든 작업이 모두 원자일 것이다. 쓰레드가 하나만 있기 때문에 간섭이 있을 수 없다.
수정이 겹칠 수 있다는 것은 데이터의 손실이 발생할 수 있다는 뜻인데,
코루틴이 다른 코루틴이 수정하고 있는 데이터를 바꿀 수 있다는 것이다.
var count = 0;
fun main() = runBlocking {
val workerA = asyncIncrement(2000)
val workerB = asyncIncrement(100)
workerA.await()
workerB.await()
println("count : ${count}")
} // End of main
private fun asyncIncrement(by : Int) = GlobalScope.async{
for(i in 0 until by) {
count++
}
} // End of asyncIncrement
실제로 위 코드는 asyncIncrement()
코루틴을 2번 동시에 실행한다.
문제는 여기서 두 실행이 서로 간섭이 일어날 수 있으며, 서로 다른 코루틴 인스턴스가 값을 재정의 할 수 있다는 것이다. main()
을 실행하면 대부분 "count : 2100"을 출력하지만, 꽤 많은 출력에서 2,100보다 적은 출력값을 볼 수 있다.
코루틴에서 명령이 중첩되는 것은 count++
작업이 원자적이지 않기 때문이다.
count++
의 원자성이 없기 때문에 두 코루틴이 다른 코루틴이 하는 조작을 무시하고 값을 읽고 수정할 수 있다.