Kotlin协程(Coroutine)中关于yield的解释

如果你在关注kotlin并且也在关注Coroutine 那么你肯定在官网上看到了 这样的代码:

val job = launch {
val child = launch {
try {
delay(Long.MAX_VALUE)
} finally {
println(“Child is cancelled”)
}
}
yield()
println(“Cancelling child”)
child.cancel()
child.join()
yield()
println(“Parent is not cancelled”)
}
job.join()

(出自kotlin官方网站教程:协程-异常处理-取消与异常)

别的都很好理解,这个yield是个啥?

如果你了解其他语言如js,你肯定了解yield是“return the value,and continue when you next enter。”就是在此处返回,并且在你下次进入时 从此处继续。

如:

function test() {

for(int i =0; i < 100; i++) {

yield i;

}

}

那么当你运行test方法时,每次输出的值就是0, 1, 2, 3,4,5 …., 99

其实在kotlin的协程中 也是差不多的意思,开始我也不是很理解,去看了看doc,发现还是不是很理解于是上网去查看了,网上的一些解答:

https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html

Yields a thread (or thread pool) of the current coroutine dispatcher to other coroutines to run. If the coroutine dispatcher does not have its own thread pool (like Dispatchers.Unconfined) then this function does nothing, but checks if the coroutine Job was completed. This suspending function is cancellable. If the Job of the current coroutine is cancelled or completed when this suspending function is invoked or while this function is waiting for dispatching, it resumes with CancellationException.

It accomplishes at least a few things

It temporarily deprioritises the current long running CPU task, giving other tasks a fair opportunity to run.
Checks whether the current job is cancelled, since otherwise in a tight CPU bound loop, the job may not check until the end.
Allows for progress of child jobs, where there is contention because more jobs than threads. This may be important where the current job should adapt based on progress of other jobs.
(出自: https://stackoverflow.com/questions/54967529/kotlin-coroutine-yield-what-is-its-purpose)

我大概翻译下这位大佬的意思:

1.暂时打断一件长耗时的任务,并且让其他任务一个公平的机会去执行

2.检查当前任务是否是被取消,这个任务可能并没有在最后检查自己是否被取消

3.当你的入栈的任务数大于当前允许并行的数目时,允许子任务执行。这个对与任务依赖很重要。

是不是还有点晕? 其实结合其他平台的yield定义,已经不是很难理解了,下面上代码:

@Test
fun main() {
val singleDispatcher = newSingleThreadContext(“Single”)

runBlocking {
val job = GlobalScope.launch {
launch {
withContext(singleDispatcher) {
repeat(3) {
printSomeThingBlock(“Task1”)
//yield()
}
}
}

launch {
withContext(singleDispatcher) {
repeat(3) {
printSomeThingBlock(“Task2”)
//yield()
}
}
}
}

job.join()
}
}

suspend fun printSomeThingBlock(text: String) {
println(text)
Thread.sleep(1000)
}

大家可以去自己执行一下,这里直接给出答案:

Task1
Task1
Task1
Task2
Task2
Task2

还是很简单的。

那么如果我们把yield的注释去掉,答案就会发生变化:

Task1
Task2
Task1
Task2
Task1
Task2

看到这里 大家是不是就清楚为什么了吧。

yield在协程中就可以简单的理解为,挂起当前任务(注意是任务),释放此线程的monitor让其他正在等待的任务公平的竞争,去获得执行权。是不是和其他语言中的yield很类似?