Kotlin-coroutine yield()的作用是什么?

时间:2019-03-03 09:45:06

标签: kotlin coroutine

我不确定yield函数的用途是什么。

您能检查一下我有这个例子吗?

尽管如此,我仍在跟踪示例here

代码如下:

val job = launch {
    val child = launch {
        try {
            delay(Long.MAX_VALUE)
        } finally {
            println("Child is cancelled")
        }
    }
    yield() //why do i need this ???????
    println("Cancelling child")
    child.cancel()
    child.join()
    yield()
    println("Parent is not cancelled")
}
job.join()

当我注释掉第一笔收益时,我得到以下结果:

  • 取消孩子

    父级未取消

但是如果我保持产量不变,我会得到:

  • 取消孩子

    孩子被取消

    父级未取消

在这里使用yield是什么意思?

6 个答案:

答案 0 :(得分:3)

在您的示例中,yield()在父作业内部被调用。它对您的父母说:“您的工作更多,请稍等,我将让其他任务工作一段时间,一段时间后,我将让您继续工作”。

因此父作业正在等待。 童工工作一段时间。 一段时间后,父作业通过yield()之后的下一行,并取消子作业。

如果在示例中未使用yield(),则父作业立即取消子作业。

让我用不同的示例来说明产量,该示例以更清晰的方式显示了产量。 2个作业在队列中等待线程等待它们工作。当您调用yield时,线程会进入队列并看到其他作业正在等待,这样它就可以使其他作业正常工作。

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.yield

fun main() = runBlocking {


    val job1 = launch {
        repeat(10) {
            delay(1000)
            println("$it. step done in job 1 ")
            yield()
        }
    }

    val job2 = launch {
        repeat(10) {
            delay(1000)
            println("$it. step done in job 2 ")
            yield()
        }
    }

    job1.join()
    job2.join()
    println("done")
}

输出:

0. step done in job 1 
0. step done in job 2 
1. step done in job 1 
1. step done in job 2 
2. step done in job 1 
2. step done in job 2 
3. step done in job 1 
3. step done in job 2 
4. step done in job 1 
4. step done in job 2 
5. step done in job 1 
5. step done in job 2 
6. step done in job 1 
6. step done in job 2 
7. step done in job 1 
7. step done in job 2 
8. step done in job 1 
8. step done in job 2 
9. step done in job 1 
9. step done in job 2 
done

答案 1 :(得分:2)

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

  

产生当前协程调度程序的线程(或线程池)   到其他协程运行。如果协程调度员没有   它自己的线程池(如Dispatchers.Unconfined),然后此函数   什么都不做,但是检查协程作业是否完成。这个   暂停功能是可以取消的。如果当前的工作   当此暂停功能启用时,协程将被取消或完成   调用,或者在此函数等待分派时恢复   带有CancellationException。

它至少可以完成几件事

  1. 它暂时取消了当前长时间运行的CPU任务的优先级,为其他任务提供了运行的机会。
  2. 检查当前作业是否被取消,因为否则在紧密的CPU绑定循环中,作业可能直到结束才检查。
  3. 允许进行子级作业,因为存在的作业比线程多,因此存在争用。在当前工作应根据其他工作的进度进行调整的情况下,这可能很重要。

答案 2 :(得分:2)

我对协程也不陌生,我对协程流程的理解是launch中的代码将像最后一次执行一样执行,就在退出main函数之前。为了优先考虑,我们使用-delay, yield, join。在此示例中,我们可以将yield更改为delay,并将得到相同的结果。

流程:

  1. 应用跳过job = launchjob.join(), 了解未来的代码正在等待工作=启动'将 完成

  2. 应用跳过child= launch进入yield()或 我们可以使用delay(10)并了解将来的代码不是 很重要,并且可以追溯到开始,所以child= launch

  3. 进入delay(Long.MAX_VALUE)这是一个陷阱

  4. 应用程序进入println("Cancelling child"),然后到达child.cancel()child.join(),后者是流触发。在这种情况下,我们可以用yield or join代替它。在该触发器应用程序之后,了解到child = launch被取消,但是finally语句未执行,并执行println("Child is cancelled")

  5. 执行yield()(我发现它没用),然后执行println("Parent is not cancelled")

    您的问题--- yield()//为什么我需要这个? 因为如果没有yield,应用程序将不会回到child= launch,也不会进入try块内,并且在代码到达child.join()之后,finally与{由于之前没有触发println("Child is cancelled")块,因此不会执行{1}}。

我建议在调试模式下运行此代码,并在每行上设置断点,并在Intellij中使用“ F9”来了解流程,并尝试在运动场中使用Alex Yu代码并更改try

答案 3 :(得分:1)

@Vasile的answer与问题最相关,@ Yuri Schimke接受的答案只是一般性信息,实际上并未回答问题。

为了说明第一个yield的需要,让我们通过添加两个“ *正在运行”语句来稍微更改代码:

val job = launch {
    val child = launch {
        try {
            println("Child is running")
            delay(Long.MAX_VALUE)
        } finally {
            println("Child is cancelled")
        }
    }
    
    yield() // without this, child job doesn't get executed
    println("Cancelling child")
    child.cancel()
    child.join()
    yield()
    println("Parent is not cancelled")
}
println("Parent is running")
job.join()

输出:

Parent is running
Child is running
Cancelling child
Child is cancelled
Parent is not cancelled

在没有第一个yield的情况下,由于子作业没有运行的机会,因此不会打印“ 子正在运行”。 delay暂停子级执行并恢复父级执行。 cancel中断delay,并将执行移至finally块中。 join和第二个yield并没有实际效果,但是通过在子作业上调用join,我们绝对确保仅在子项完成/取消后才执行以下任何代码

答案 4 :(得分:0)

经过一些研究,我发现yield实际上来自java,而术语yield a thread是我不理解的。

本质上:yield()基本上意味着线程没有做任何重要的事情,如果需要运行其他线程,则可以运行它们。 (我更喜欢使用Alex Yu提到的join)。基本上,如果我们要可视化yield的执行情况...。无论您调用yield的线程将被推送到消息传递队列的后面,然后具有相同优先级的其他线程将在该线程之前执行。所以就像去俱乐部后面。

答案 5 :(得分:-1)

suspend fun yield(): Unit (source)

Yields当前coroutine调度程序的线程(或线程池)到其他coroutine的运行(如果可能)。

该暂停功能是可以取消的。如果在调用此挂起功能时或在此函数等待分派时当前coroutine的作业被取消或完成,它将以CancellationException恢复。

注意: 此功能始终会检查取消,即使它没有挂起。