본문 바로가기

카테고리 없음

Coroutine 3부

반응형
  • 코루틴은 Dispatcher.Default와 같은 멀티 스레드 디스패쳐를 통해서 동시에 수행되도록 만들수 있다.
  • 이러한 상황에서 주로 발생하는 문제는 동기화 이슈가 있으며 일반적인 멀티 스레드에서의 동기화 해결책들과 비슷하지만 코루틴 고유의 것들도 존재 (Actor)
  •  
  • Volatile [공유객체]
    • 하나의 객체를 여러 개의 쓰레드가 함께 사용하거나 공유한다는 것을 의미
    • ex) 놀이터에 그네가 하나 있고, 어린 아이들은 셋 있다. 이런 상황에서 그네를 [공유 객체], 아이들을 [Thread]라고 말할 수 있다.
    • 클래스의 멤버변수는 heap 메모리에 존재하므로 스레드가 공유하여 접근할 수 있으며 이때 각 스레드는 속도향상을 위해 메인 메모리에 접근해서 값을 가져가는게 아닌, 캐시에서 변수값을 읽어가며 쓰기 또한 캐시값을 이용, 어떤 시점에 변경된 캐시 값에 메인 메모리에 업데이트 된다.
    • 따라서 멀티 스레드가 해당 변수의 값을 읽거나 쓰면 각각 자신의 캐시에서 값을 읽고 쓰고, 어떤 시점에서 변경된 값을 메인 메모리에 업데이트 하기 때문에 실제 원하는 갑솨 달라지게 된다.
    • 이러한 문제를 막기위한 키워드가 volatile이며 해당 키워드는 접근 가능한 변수의 값을 캐시를 통해 사용하지 않고 스레드가 직접 메인 메모리에 접근하여 읽고 쓴다.
    • 하지만 volatile로도 완벽한 동기화를 할 수 없으며 문제점도 있다.
      • 캐시를 이용하지 않고 메인 메모리에 직접 액세스 하기 때문에 더 비싼 코스트를 지불
      • volatile 변수는 읽기 쓰기가 JVM에 의해 리오더링 되지 않는다.
      • volatile 변수는 리드시 항상 최신값을 반환하지만 여러 쓰레드가 동시 읽기, 쓰기를 하면 쓰기 시점을 알수 없기 때문에 여전히 동기화 문제가 일어난다.
      class Worker : Thread() {
          @Volatile
          var stop = false
          override fun run() {
              super.run()
              while(!stop){ }
          }
      }
      
  • Mute
    • 여러 스레드를 실행하는 환경에서 자원에 대한 접근 제한을 강제하기 위한 동기화 매커니즘
    • ex) 화장실에 있는 유일한 변기칸(공유된 자원)
      1. 대변이 마려운 한명이 변기칸에 들어감
      2. 들어간 사람은 이제 변기칸에 다른 사람이 들어오지 못하도록 문을 걸어 잠굼(Lock)
      3. 대변이 마려운 새로운 사람이 화장실 도착, 유일한 변기칸에 진입을 시도
      4. 기존에 들어간 사람이 나올 때까지 기다려야 함
      5. 기존에 들어간 사람이 나올 때까지 기다리는 사람이 더 증가
    • 자바에서는 Synchronized나 ReentrantLock을 이용하여 critical section을 블락시킨다.
    • 코루틴에서는 lock과 unlock 메소드를 가지는 Mutex를 이용하며 Mutex.lock()은 suspend function이다.
    suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
    ... // do something
    }
    
    val mutex = Mutex()
    var counter = 0
    
    fun main() = runBlocking {
        GlobalScope.massiveRun {
            mutex.withLock { // 상호ㅓ배제를 사용한 보호
                counter++        
            }
        }
        println("Counter = $counter")
    }
    
    
    mutex.lock()
    // 보호하고자 하는 임계 구역 코드
    try {
        ...
    } finally {
        mutex.unlock()
    }    
    
  • 이러한 예제는 작은 단위의 스레드 한정이므로 성능면에서는 손실이 있지만 특정 상황에서는 괜찮은 선택이 될 수 있다.
  • ex) 공유되는 상태를 어쩔 수 없이 주기적으로 변경해야 하고 이를 위한 스레드 한정은 적용하기 어려운 경우
  • Actor
    • 코루틴에서 쓰레드간 동기화를 지원하기 위한 도구
    • 코루틴과 코루틴 내부로 캡슐화 된 상태값, 그리고 다른 코루틴과 통신할 수 있는 채널의 조합으로 구성 되어 있다.
      • 상태 엑세스를 단일 스레드로 한정한다.
      • 다른 쓰레드는 채널을 통해서 상태 수정을 요청한다.
    • 다른 언어의 actor 개념은 들어오고 나가는 메일 박스 기능과 비슷하지만 코틀린에서는 들어오는 메일 박스 기능만 한다고 볼 수 있다.
    • 액터는 한 번에 1개의 메시지만 처리하는 것을 보장하며 어떤 특정 상태를 관리하기 위한 백그라운드 테스크에 유용함
    • 채널과 같기 때문에 메시지를 보낼 때 send()를 사용한다.
    val actorCounter = actor<Void?> {
        for (msg in channel) {
            counter++
        }
    }
    
    val workA = async (context){
    
        repeat(2000) {
            actorCounter.send(null)
        }
    }
    
    val workB = async (context){
    
        repeat(300) {
            actorCounter.send(null)
        }
    }
    
    workA.await()
    workB.await()
    
    println(counter)
    
  •  Flow
    • 코루틴의 플로우는 데이터 스트림이며, 코루틴 상에서 리액티브 프로그래밍을 지원 하기 위한 구성요소이다.
    • 기존 명령형 프로그래밍에서는 데이터의 소비자는 데이터를 요청한 후 받은 결과값을 일회성으로 수신하지만 이러한 방식은 데이터가 필요할 때마다 결과값을 매번 요청해야 한다는 점에서 매우 비효율적이다.
    • 리액티브 프로그래밍에서는 데이터를 발행하는 발행자가 있고 데이터의 소비자는 데이터의 발행자에 구독 요청을 하고 데이터의 발행자는 새로운 데이터가 들어오면 데이터의 소비자에게 지속적으로 발행한다.
    • 즉, 리액티브 프로그래밍에는 하나의 데이터를 발행하는 발행자가 있고 해당 발행자는 데이터의 소비자에게 지속적으로 데이터를 전달하는 역할을 한다. 이것을 데이터 스트림이라고 한다.

  • 데이터 스트림은 아래 세가지로 구성되며 flow의 핵심 구성 요소이다.
    • Producer(생산자)
    • Intermediary(중간 연산자)
    • Consumer(소비자)

fun getBroadCastInfo(id: String): Flow<BroadCastInfo) {
	return flow {
		val data = sauceLiveApi.getBroadCastInfo(id)
		emit(data)
}

 

viewModelScope.launch {
	playerRepository.getBroadCastInfo(id) 
		.flowOn(Dispatchers.IO)
		.catch {}
		.collect {}
}

https://velog.io/@wijoonwu/Thread-와-공유객체

https://drcode-devblog.tistory.com/297

https://www.charlezz.com/?p=45959

https://myungpyo.medium.com/코루틴-공식-가이드-자세히-읽기-part-8-1b434772a100

https://origogi.github.io/coroutine/액터/

https://kotlinworld.com/175

https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/

반응형