延迟函数如何在不阻塞当前线程的情况下在Kotlin中工作?

延迟函数如何在不阻塞当前线程的情况下在Kotlin中工作?,kotlin,coroutine,Kotlin,Coroutine,过去几天我在学习协同程序,大多数概念都很清楚,但我不了解延迟函数的实现 延迟时间过后,延迟功能如何恢复协同程序?对于一个简单的程序,只有一个主线程,为了在延迟时间之后恢复协同路由,我假设应该有另一个计时器线程来处理所有延迟的调用,并在以后调用它们。这是真的吗?有人能解释一下延迟函数的实现细节吗?所有挂起函数的工作方式都是一样的,当编译时,它会被转换成带有回调的状态机 调用delay时,发生的情况是消息以一定的延迟发布到队列上,类似于Handler().postDelayed(delay),当延迟

过去几天我在学习协同程序,大多数概念都很清楚,但我不了解延迟函数的实现


延迟时间过后,延迟功能如何恢复协同程序?对于一个简单的程序,只有一个主线程,为了在延迟时间之后恢复协同路由,我假设应该有另一个计时器线程来处理所有延迟的调用,并在以后调用它们。这是真的吗?有人能解释一下延迟函数的实现细节吗?

所有挂起函数的工作方式都是一样的,当编译时,它会被转换成带有回调的状态机

调用
delay
时,发生的情况是消息以一定的延迟发布到队列上,类似于
Handler().postDelayed(delay)
,当延迟消失时,它会回调暂停点并恢复执行

您可以查看
delay
函数的源代码,了解其工作原理:

public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
    }
}
公共暂停乐趣延迟(timeMillis:Long){
如果太长了,读不下去了;
当使用runBlocking时,延迟会在内部包装并在同一线程上运行,当使用任何其他dispatcher时,延迟会挂起,并通过事件循环线程恢复继续来恢复。请检查下面的详细答案以了解内部情况

长答覆: @Francesc的回答是正确的,但有点抽象,仍然没有解释延迟实际上是如何在内部工作的

因此,正如他指出的延迟函数:

公共暂停乐趣延迟(timeMillis:Long){
if(timeMillis)
继续。取消(任务)
时间表(现在,任务)
}
}
}
这就是
scheduleResumeAfterDelay
的实现方式。它创建了一个
DelayedResumeTask
,其延续通过delay传递,然后调用
schedule(now,task)
调用
scheduleImpl(now,delayedTask)
最后调用
delayedTask.scheduleTask(now,delayedQueue,This)
在对象中传递
delayedQueue

@已同步
有趣的scheduleTask(现在:Long,delayed:DelayedTaskQueue,eventLoop:EventLoopImplBase):Int{
如果(_heap==kotlinx.coroutines.DISPOSED_TASK)返回计划_DISPOSED//don add--已被释放
delayed.addLastIf(this){firstTask->
if(eventLoop.isCompleted)返回计划\u已完成//来自scheduleTask的非本地返回
/**
*我们即将添加新任务,必须确保[DelayedTaskQueue]
*保持不变。此lambda中的代码在
*这里的[DelayedTaskQueue]锁和使用[DelayedTaskQueue.timeNow]是线程安全的。
*/
if(firstTask==null){
/**
*添加第一个延迟任务时,我们只需将队列的[DelayedTaskQueue.timeNow]更新为
*当前时间,即使这意味着“时间倒退”。这使得结构
*在所有延迟的任务完成后,尽管“nanoTime()”测量值有很大的跳跃,但仍会进行自我校正
*从延迟队列中删除以执行。
*/
delayed.timeNow=now
}否则{
/**
*仔细更新[DelayedTaskQueue.timeNow],使其不会扫过first的任务时间
*我们不能让它在时间上倒退,也不能让它保持不变
*已计划的任务违反了。
*/
val firstTime=firstTask.nanoTime
//使用包装安全检查计算最小值(现在,第一次)
val minTime=if(firstTime-now>=0)now else firstTime
//仅在及时向前时更新timeNow
如果(minTime-delayed.timeNow>0)delayed.timeNow=minTime
}
/**
*此处[DelayedTaskQueue.timeNow]已被修改,我们必须再次检查新添加的
*任务不会因此违反[DelayedTaskQueue]不变量。请注意,此scheduleTask任务
*可以调用函数来从一个队列重新调度到另一个队列,这可能是另一个原因
*新任务的时间现在可能违反不变量。
*我们只需将此任务的时间更改为现在,即可纠正不变冲突(如果有)。
*/
如果(nanoTime-delayed.timeNow<0)nanoTime=delayed.timeNow
真的
}
返回时间表(正常)
}
它最终使用当前时间将任务设置为
DelayedTaskQueue

//内置DefaultExecutor
覆盖趣味跑(){
ThreadLocalEventLoop.setEventLoop(此)
registerTimeLoopThread()
试一试{
var ShutdownNano=长最大值
如果(!DefaultExecutor.notifyStartup())返回
while(true){
Thread.interrupted()//只需重置中断标志
var parknos=DefaultExecutor.processNextEvent()/*注意,它在这里调用processNextEvent*/
如果(Parknos==长的最大值){
//无需执行任何操作,初始化关机超时
if(shutdownnonas==Long.MAX_值){
val now=nanoTime()
如果(shutdownnonas==Long.MAX_值)shutdownnonas=now+DefaultExecutor.KEEP_ALIVE_nano
val tillShutdown=shutdownnos-现在
如果(0){
//在这种情况下,检查是否请求关闭和解除
如果(DefaultExecutor.isShutdownRequested)返回
帕克纳诺(这个,帕克纳诺)
}
}
}最后{
DefaultExecutor.\u thread=null//此线程已失效
DefaultExecutor.AcknowledgeshutdownifRequired()
取消注册TimeLoopThread()
//重新检查_线程引用后队列是否为空