소개

6장_채널 - 통신을 통한 메모리 공유

채널- 코루틴 간 공유 메모리 동기화 문제 해결
코루틴 간 안전한 데이터 통신 - 메모리 주소가 아니라 메시지 사용
상태 공유에 따른 교착 상태 진입, 일관성 상실 등을 방지
데드락
레이스 컨디션
원자성 위반
채널 활용 사례
스트리밍 데이터
다수 데이터 소스로부터 네트워크 데이터 요청 / 수신
데이터 소스별 응답성 차이
awaitAll의 경우 가장 늦은 응답에 맞춰 동기화
채널로 상태 변경 경로를 단일화하고, 데이터 소스별로 응답이 올때마다 메시지로 전달
멀티 프로듀서, 싱글 컨슈머 (이벤트 디스패칭 방식)
분산 작업
네트워크 데이터 요청 + 연산
네트워크 요청 시간 : 데이터 연산의 소요 시간 = 1 : 2
네트워크 요청 쓰레드 : 데이터 연산 쓰레드 = 1 : 2
채널로 네트워크 요청을 통해 가져온 데이터를 데이터 연산 쓰레드로 안전하게 전달
멀티 프로듀서, 멀티 컨슈머
채널 유형
채널에 여러 유형이 존재 하는 이유
프로듀서와 컨슈머의 속도 차이의 정도와 대응 방식이 다름
컨슈머가 처리할 수 있는 이상으로 프로듀셔가 생산하는 경우 문제 발생(OOM, RateLimit 등)
언버퍼드 채널
버퍼가 없음
receive로 소비할 때 비로소 send로 생산
버퍼드 채널
LinkedListChannel (무한)
ArrayChannel (유한)
ConflatedChannel (덮어쓰기)
활용예
override fun onCreate(savedInstanceState: Bundle?) { ... GlobalScope.launch { loadMore() } }
JavaScript
복사
private suspend fun search() { val query = findViewById<EditText>(R.id.searchText) .text.toString() val channel = searcher.search(query) while (!channel.isClosedForReceive) { val article = channel.receive() GlobalScope.launch(Dispatchers.Main) { viewAdapter.add(article) } } }
JavaScript
복사
class Searcher { private val dispatcher = newFixedThreadPoolContext(3, "IO") private val factory = DocumentBuilderFactory.newInstance() private val feeds = listOf( Feed("npr", "https://www.npr.org/rss/rss.php?id=1001") Feed("cnn", "http://rss.cnn.com/rss/cnn_topstories.rss"), Feed("fox", "http://feeds.foxnews.com/foxnews/latest?format=xml") ) fun search(query: String) : ReceiveChannel<Article> { val channel= Channel<Article>(150) feeds.forEach { feed -> GlobalScope.launch(dispatcher) { search(feed, channel, query) } } return channel } private suspend fun search(feed: Feed, channel: SendChannel<Article>, query: String) { val builder = factory.newDocumentBuilder() val xml = builder.parse(feed.url) val news = xml.getElementsByTagName("channel").item(0) Log.d("Searcher", "Looking for $query") (0 until news.childNodes.length) .map { news.childNodes.item(it) } .filter { Node.ELEMENT_NODE == it.nodeType } .map { it as Element } .filter { "item" == it.tagName } .forEach { val title = it.getElementsByTagName("title") .item(0) .textContent var summary = it.getElementsByTagName("description") .item(0) .textContent if (title.contains(query) || summary.contains(query)) { if (summary.contains("<div")) { summary = summary.substring(0, summary.indexOf("<div")) } val article = Article(feed.name, title, summary) channel.send(article) } } } }
JavaScript
복사