Kotlin 협 정 은 도대체 어떻게 스 레 드 를 전환 합 니까?
그런데 협 정 은 도대체 뭘 까요?
협 정 은 사실 오래된 개념 으로 이미 매우 성숙 하 였 으 나,모두 가 그것 의 개념 에 대해 줄곧 각종 의문 을 가지 고 있어,많은 사람들 이 잇달아 말 하고 있다.
어떤 사람 은 협 정 이 경량급 의 스 레 드 라 고 말 하고,또 어떤 사람 은 kotlin 협 정 이 사실은 본질 적 으로 스 레 드 전환 방안 이 라 고 말한다.
분명히 이것 은 초보 자 에 게 그다지 우호 적 이지 않다.한 가지 물건 이 무엇 인지 잘 모 를 때 왜,어떻게 하 는 단계 에 들 어가 기 어렵다.
본 고 는 주로 이 문제 에 대답 하 는데 주로 다음 과 같은 내용 을 포함한다.
1.협 정 에 대한 선행 지식
2.협 정 은 도대체 무엇 입 니까?
3.kotlin 협회 의 기본 개념,걸 기 함수,CPS 변환,상태 기 등
상기 문 제 는 사고 지도 로 요약 하면 다음 과 같다.
1.선행 지식
1.1
CoroutineScope
도대체 무엇 입 니까?CoroutineScope
즉,협 정 운행 의 역할 영역 으로 소스 코드 는 매우 간단 하 다.
public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}
이 를 통 해 알 수 있 듯 이CoroutineScope
의 코드 는 매우 간단 하고 주요 역할 은 제공CoroutineContext
,협 정 운행 의 문맥 이다.우리 가 흔히 볼 수 있 는 실현 은
GlobalScope
,LifecycleScope
등 이 있다.1.2
ViewModelScope
와GlobalScope
는 어떤 차이 가 있 습 니까?
public object GlobalScope : CoroutineScope {
/**
* [EmptyCoroutineContext].
*/
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
양자 의 코드 는 모두 매우 간단 해서 위 에서 볼 수 있다1.
ViewModelScope
되 돌아 오 는 것 은GlobalScope
의 공 실현 이다.2.
CoroutineContext
는ViewModelScope
에CoroutineContext
과Job
를 추가 했다.저희 가 먼저 간단 한 코드 를 보 겠 습 니 다.
fun testOne(){
GlobalScope.launch {
print("1:" + Thread.currentThread().name)
delay(1000)
print("2:" + Thread.currentThread().name)
}
}
// :DefaultDispatcher-worker-1
fun testTwo(){
viewModelScope.launch {
print("1:" + Thread.currentThread().name)
delay(1000)
print("2:" + Thread.currentThread().name)
}
}
// : main
위의 두 가지Dispatcher
가 협 정 을 시작 한 후에 현재 스 레 드 이름 을 인쇄 하 는 것 은 다 릅 니 다.하 나 는 스 레 드 탱크 의 스 레 드 이 고 하 나 는 메 인 스 레 드 입 니 다.Scope
에ViewModelScope
를 첨가 한 이유 다.우 리 는 협 정 은
CoroutineContext
스케줄 러 를 통 해 스 레 드 전환 을 제어 한 다 는 결론 을 얻 을 수 있다.1,3 스케줄 러 가 뭐 예요?
사용 상 스케줄 러 는 우리 가 사용 하 는 것
Dispatchers.Main.immediate
,Dispatchers
,Dispatchers.Main
등 이다.역할 상 스케줄 러 의 역할 은 협 정 운행 을 제어 하 는 스 레 드 이다.
구조 적 으로 볼 때
Dispatchers.Default
의 부 류 는Dispatcher.IO
이 고 그 다음 에Dispatchers
에 계승 된다.그들의 유형 구조 관 계 는 다음 과 같다.
이것 도
ContinuationInterceptor
가CoroutineContext
에 가입 할 수 있 는 이유 이 며,Dispatchers
조작 부호 가 증가 하 는 것 을 지원 하 는 이유 이다.1.4 차단기 란 무엇 인가
이름 에서 쉽게 알 수 있 듯 이
CoroutineContext
즉 협 정 차단기 입 니 다.먼저 인 터 페 이 스 를 보 세 요.
interface ContinuationInterceptor : CoroutineContext.Element {
// ContinuationInterceptor CoroutineContext Key
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
/**
* continuation
*/
fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
//...
}
위 에서 두 가지 정 보 를 추출 할 수 있다.1.차단기
+
는 하나의 예 이기 때문에 여러 개의 차단 기 를 추가 할 때 한 개 만 적 용 됩 니 다.2.우 리 는 모두 알 고 있다.
ContinuationInterceptor
그Key
방법 을 호출 하면 그Continuation
수식 함수 의 코드 블록 을 실행 할 것 이다.만약 에 우리 가 미리 차단 하면 다른 일 을 할 수 있 지 않 을 까?이것 이 바로 스케줄 러 가 스 레 드 를 전환 하 는 원리 이다.위 에서 우 리 는 이미
Continuation#resumeWith()
지정 협 정 을 통 해 운행 하 는 스 레 드 를 소개 하 였 으 며,suspend
협 정 이 회복 되 기 전에 차단 하여 스 레 드 를 전환 하 였 다.이러한 선행 지식 을 가지 고 우 리 는 협 정 이 작 동 하 는 구체 적 인 절 차 를 살 펴 보고 협 정 전환 스 레 드 소스 코드 의 구체 적 인 실현 을 명 확 히 한다.
2.협 정 스 레 드 전환 소스 코드 분석
2.1
Dispatchers
방법 분석우 리 는 먼저 협 정 이 어떻게 작 동 하 는 지,어떤 매개 변수 가 들 어 왔 는 지 살 펴 보 자.
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
총 3 개의 인자 가 있 습 니 다:1.들 어 오 는 협정 상하 문
2.
interceptContinuation
시동 기 는 매 거 진 클래스 로 서로 다른 시작 방법 을 정 의 했 습 니 다.기본 값 은launch
입 니 다.3.
CoroutinStart
바로 우리 가 들 어 온 협의 체 이 고 진정 으로 실행 해 야 할 코드 이다.이 코드 는 주로 두 가지 일 을 했다.
1.새로운 조합
CoroutineStart.DEFAULT
2.Continuation 만 들 기2.1.1 새로운 조합
block
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
val combined = coroutineContext + context
val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
debug + Dispatchers.Default else debug
}
위 에서 아래 정 보 를 추출 할 수 있 습 니 다:1.
CoroutineContext
방법 이 들 어 오 는CoroutineContext
방법 과launch
중의context
방법 을 조합 합 니 다.2.
CoroutineScope
에 차단기 가 없 으 면 기본 차단기,즉context
가 들 어 옵 니 다.이것 은 우리 가 차단기 에 들 어 오지 않 았 을 때 기본 스 레 드 전환 효과 가 있 는 이 유 를 설명 합 니 다.2.1.2 하나 만 들 기
combined
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
기본 적 인 상황 에서 우 리 는 하 나 를 만 들 것 이다Dispatchers.Default
주의해 야 할 것 은 이것Continuation
은 사실 우리 협의 체 의StandloneCoroutine
,즉 성공 후의 반전 이지 협의 체 자체 가 아니다.그리고 호출
coroutine
은 협 정 이 시작 되 었 음 을 나타 낸다.2.2 협상 의 시작
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
initParentJob()
start(block, receiver, this)
}
이 어 협 정 을 시작 하기 위해complete
의coroutine.start
를 호출 했다.기본 적 인 상황 에서 호출 된 것 은CoroutineStart
이다.층 층 이 호출 되 어 마지막 에 도 착 했 습 니 다.
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(receiver: R, completion: Continuation<T>) =
runSafely(completion) {
// Coroutine
createCoroutineUnintercepted(receiver, completion)
// ,
.intercepted()
// resumeWith
.resumeCancellableWith(Result.success(Unit))
}
여기 가 바로 협 정 이 시작 하 는 핵심 코드 입 니 다.짧 지만 세 가지 절 차 를 포함 합 니 다.1.협의 체 창설
start
2.차단 만 들 기CoroutineStart.Default
,즉Continuation
3.실행Continuation
방법2.3 협의 체 창설
DispatchedContinuation
호출DispatchedContinuation.resumeWith
은 우리 의 협의 체 즉Continuation
을createCoroutineUnintercepted
로 전환 시 킬 것 이다.그것 은suspend block
이 고 계승Continuation
이다.SuspendLambda
방법 은 소스 코드 에서 구체 적 인 실현 을 찾 을 수 없 지만 협의 체 코드 를 역 컴 파일 하면 진정한 실현 을 볼 수 있다.자세 한 내용 은 다음 과 같 습 니 다:바이트 코드 역 컴 파일
2.4 창설
ContinuationImpl
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
(this as? ContinuationImpl)?.intercepted() ?: this
//ContinuationImpl
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
//CoroutineDispatcher
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
위 에서 아래 의 정 보 를 추출 할 수 있다1.
createCoroutineUnintercepted
확장 방법 입 니 다.마지막 으로DispatchedContinuation
방법 으로 호출 됩 니 다.2.
interepted
에서ContinuationImpl.intercepted
를 이용 하여 현재 차단 기 를 가 져 옵 니 다.3.현재 차단 기 는
intercepted
이기 때문에 최종 적 으로 하나CoroutineContext
로 돌아 갑 니 다.우 리 는 사실 이 를 이용 하여 스 레 드 전환 을 실현 합 니 다.4.우 리 는 협의 체
CoroutineDispatcher
를DispatchedContinuation
에 전래 시 켰 는데 여기 서 실 용적 으로Continuation
기능 의 강 화 를 실현 했다.여기 서 분명 하 다.
DispatchedContinuation
을 통 해 기 존의 협 정 을 장식 하고
에서 스케줄 러 처리 스 레 드 전환 을 통 해 기 존의 논리 에 영향 을 주지 않 고 기능 의 강 화 를 실현 한다.2.5 차단 처리
//DispatchedContinuation
inline fun resumeCancellableWith(
result: Result<T>,
noinline onCancellation: ((cause: Throwable) -> Unit)?
) {
val state = result.toState(onCancellation)
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled(state)) {
resumeUndispatchedWith(result)
}
}
}
}
시작 할 때 호출DispatchedContinuation
하 는DispatchedContinuation
방법 에 대해 말씀 드 렸 습 니 다.이 안에서 하 는 일 도 간단 하 다.
1.스 레 드 전환 이 필요 하 다 면
DispatchedContinuation
방법 을 호출 합 니 다.여기resumeCancellableWith
는dispatcher.dispatcher
을 통 해 추출 되 었 습 니 다.2.스 레 드 전환 이 필요 하지 않 으 면 기 존 스 레 드 를 직접 실행 하면 됩 니 다.
2.5.2 스케줄 러 의 구체 적 인 실현
우 리 는 먼저 명확 하 게
dispatcher
는CoroutineConext
을 통 해 추출 한 것 이 고 이것 도 협 정 문맥 작용 의 표현 이다.CoroutineDispatcher
공식 적 으로 네 가지 실현 을 제공 했다.CoroutineContext
,CoroutineDispater
,Dispatchers.Main
,Dispatchers.IO
우리 함께 간단하게Dispatchers.Default
의 실현 을 봅 시다.
internal class HandlerContext private constructor(
private val handler: Handler,
private val name: String?,
private val invokeImmediately: Boolean
) : HandlerDispatcher(), Delay {
public constructor(
handler: Handler,
name: String? = null
) : this(handler, name, false)
//...
override fun dispatch(context: CoroutineContext, block: Runnable) {
// Handler
handler.post(block)
}
}
볼 수 있 듯 이 사실은Dispatchers.Unconfined
으로 메 인 스 레 드 로 전환 되 었 다.Dispatchers.Main
를 사용 해도 마찬가지 입 니 다.스 레 드 풀 로 바 뀌 었 을 뿐 입 니 다.위 에서 보 듯 이 사실은 장식 모델 이다.
1.호출
handler
방법 으로 스 레 드 전환2.전환 완료 후 호출
Dispatcers.IO
방법 으로 진정한 협 정 체 를 실행 합 니 다.3
CoroutinDispatcher.dispatch
스 레 드 를 어떻게 전환 합 니까?위 에서 우 리 는 협 정 스 레 드 스케줄 의 기본 원리 와 실현 을 소개 하 였 으 며,아래 에서 우 리 는 몇 가지 작은 문제 에 대답 하 겠 다.
우 리 는
DispatchedTask.run
함수 가 끊 어 진 후에 한동안 기 다 렸 다가 다시 회복 할 것 이라는 것 을 안다.이 안에 도 스 레 드 의 전환 과 관련 이 있 을 것 이 라 고 상상 할 수 있 습 니 다.구체 적 으로 어떻게 실현 되 었 습 니까?
public suspend fun delay(timeMillis: Long) {
if (timeMillis <= 0) return // don't delay
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
// if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
if (timeMillis < Long.MAX_VALUE) {
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
}
internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay
delay
의 코드 도 간단 하여 위 에서 다음 과 같은 정 보 를 추출 할 수 있다.delay
의 전환 도 차단 기 를 통 해 이 루어 졌 고 내 장 된 차단 기 는 동시에Dealy
인 터 페 이 스 를 실현 했다.구체 적 인 실현 을 살 펴 보 겠 습 니 다.
internal class HandlerContext private constructor(
private val handler: Handler,
private val name: String?,
private val invokeImmediately: Boolean
) : HandlerDispatcher(), Delay {
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
// Handler , continuation
val block = Runnable {
with(continuation) { resumeUndispatched(Unit) }
}
handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))
continuation.invokeOnCancellation { handler.removeCallbacks(block) }
}
//..
}
1.알 수 있 듯 이 사실delay
을 통 해 지연 효 과 를 실현 한 것 이다.2.시간 이 되면
Delay
방법 으로 협상 을 재 개 합 니 다.3.만약 에 우리 가 사용 하 는 것 이
handler.postDelayed
이 라면 효과 도 똑 같 습 니 다.다른 것 은 지연 효 과 는 스 레 드 전환 을 통 해 이 루어 진 것 입 니 다.4.
resumeUndispatched
스 레 드 를 어떻게 전환 합 니까?우 리 는 협 정 체 내 에서
Dispatcher.IO
방법 으로 간단 하고 편리 한 스 레 드 전환 을 통 해 동기 화 된 방식 으로 비동기 코드 를 쓸 수 있다.이것 도withContext
협 정의 주요 장점 중 하나 이다.
fun test(){
viewModelScope.launch(Dispatchers.Main) {
print("1:" + Thread.currentThread().name)
withContext(Dispatchers.IO){
delay(1000)
print("2:" + Thread.currentThread().name)
}
print("3:" + Thread.currentThread().name)
}
}
//1,2,3 main,DefaultDispatcher-worker-1,main
이 코드 가 스 레 드 를 전환 한 후에 다시 전환 하 는 작업 을 한 것 을 알 수 있 습 니 다.우 리 는 두 가지 문 제 를 제기 할 수 있 습 니 다.1.
withContext
스 레 드 를 어떻게 전환 합 니까?2.
kotin
안의 협의 체 가 끝 난 후에 스 레 드 는 어떻게withContext
로 전환 합 니까?
public suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T {
return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
// context
val oldContext = uCont.context
val newContext = oldContext + context
....
// Dispatcher,
val coroutine = DispatchedCoroutine(newContext, uCont)
coroutine.initParentJob()
//DispatchedCoroutine complete
block.startCoroutineCancellable(coroutine, coroutine)
coroutine.getResult()
}
}
private class DispatchedCoroutine<in T>(
context: CoroutineContext,
uCont: Continuation<T>
) : ScopeCoroutine<T>(context, uCont) {
// complete
override fun afterCompletion(state: Any?) {
afterResume(state)
}
override fun afterResume(state: Any?) {
//uCont ,context context,
uCont.intercepted().resumeCancellableWith(recoverResult(state, uCont))
}
}
이 코드 는 사실 매우 간단 해서 다음 과 같은 정 보 를 추출 할 수 있다.1.
withContext
사실은 한 층Dispatchers.Main
포장 이 고 마지막 에withContext
로 호출 되 었 습 니 다.이것 은Api
뒤의 절차 와 같 습 니 다.우 리 는 계속 따라 가지 않 겠 습 니 다.2.들 어 오 는
startCoroutineCancellable
외부 차단 기 를 덮어 하나의launch
를 생 성하 기 때문에 스 레 드 전환 이 가능 합 니 다.3.
context
는newContext
협의 체 의 생 성 함수 로 서 협의 체 의 실행 이 완료 되면DispatchedCoroutine
으로 되 돌아 갑 니 다.4.
complete
에서 들 어 오 는afterCompletion
은 부 협 정 이 고 그의 차단 기 는 외부 차단 기 이기 때문에 원래 의 스 레 드 로 전환 합 니 다.총결산
본 고 는 주로
DispatchedCoroutine
협 정 이 어떻게 스 레 드 를 전환 하 는 지 에 대한 이 문 제 를 대답 하고 소스 코드 를 분석 했다.쉽게 말 하면 다음 과 같은 절 차 를 포함한다.
1.
uCont
에kotlin
를 추가 하고 실행 할 협 정 을 지정 합 니 다.2.시작 할 때
CoroutineContext
를Dispatcher
로 만 들 고 호출suspend block
생 성Continuation
3.intercepted
바로 기 존의 협 정 에 대한 장식 이다.여기 서 호출DispatchedContinuation
스 레 드 전환 임 무 를 완성 한 후에DispatchedContinuation
장 식 된 협 정 은 협 정 체 내의 코드 를 집행 할 것 이다.사실
Dispatcher
협 정 은 장식 기 모델 로 스 레 드 전환 을 실현 하 는 것 이다.보아하니 많은 코드 가 있 는 것 같 지만 진정한 사고방식 은 사실 매우 간단 하 다.이것 은 아마도 디자인 모델 의 작용 일 것 이다
마지막.
안 드 로 이 드 개발 과 관련 된 학습 문서,면접 문제,안 드 로 이 드 핵심 노트 등 문 서 를 공유 하고 여러분 의 학습 향상 에 도움 이 되 기 를 바 랍 니 다.참고 할 것 이 있 으 면 저 에 게 CodeChina 주 소 를 직접 가 주 십시오.https://codechina.csdn.net/u012165769/Android-T3 방문 하여 열람 하 다.만약 본문 이 당신 에 게 도움 이 된다 면,좋아요 모음 집 을 클릭 하 세 요~
Kotlin 협 정 이 스 레 드 를 어떻게 전환 하 는 지 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 협 정 스 레 드 전환 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
머티리얼 디자인 도입 직후에 할 일안드로이드 프로젝트에 머티리얼 디자인을 도입한 후에 할 일을 적는다. Android 프로젝트를 만든 후 Hello world에 대해 수행합니다. 머티리얼 디자인을 도입하기 위해, build.gradle 를 이하와 같...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.