我正在尝试做一个函数来触发一个不可能被取消的缓慢的操作。我要这个行动在有超时的协同线中运行。由于操作不能像前面提到的那样被取消,所以我需要函数在超时后返回,但是操作要保持在后台。
我一直试图获得工作的代码异步运行10秒的长操作,时间为5秒,因此函数应该在超时后返回,让main继续其工作,打印"foo执行完成“,最后再打印5秒后,缓慢的作业将打印”作业结束(10秒通过)“。
下面是代码:
fun main() {
println("program execution begins")
foo()
println("foo execution finished")
while(true);
}
fun foo() = runBlocking {
val job = async {
val endTimeMillis = System.currentTimeMillis() + (10 * 1000)
while (System.currentTimeMillis() <= endTimeMillis); //blocks for 10 seconds
println("job ends (10 seconds passed)")
}
try {
withTimeout(5000) {
println("start awaiting with 5 secs timeout")
job.await()
}
} catch (ex: TimeoutCancellationException) {
println("out of time")
}
}然后产生以下结果:
program execution begins
start awaiting with 5 secs timeout
job ends (10 seconds passed)
out of time
foo execution finished但这并不是我之前提到的情况下所需要的行为。我需要这样做才能使输出看起来像:
program execution begins
start awaiting with 5 secs timeout
out of time
foo execution finished
job ends (10 seconds passed)除此之外,我不能在异步中使用任何类型的"kotlin-coroutines“函数来存档这种行为(好吧,配合取消),因为调用的代码将是与协同过程无关的用户代码,可能是用Java编写的。因此,用于阻塞异步块而不是示例中的延迟()的while循环。
提前感谢您的帮助!
发布于 2021-02-14 03:42:44
如果不能中断阻塞代码,则需要在另一个线程中运行它。否则,您的线程将没有任何机会处理超时。
此外,您还需要确保包含阻塞代码的Job不是等待的Job的子级。否则超时将取消阻塞Job,但它仍将旋转10秒,runBlocking将等待它完成。
做这两件事的最简单的方法是使用GlobalScope,如下所示:
fun foo() = runBlocking {
try {
withTimeout(5000) {
println("start awaiting with 5 secs timeout")
GlobalScope.async {
val endTimeMillis = System.currentTimeMillis() + (10 * 1000)
while (System.currentTimeMillis() <= endTimeMillis); //blocks for 10 seconds
println("job ends (10 seconds passed)")
}.await()
}
} catch (ex: TimeoutCancellationException) {
println("out of time")
}
}当然,即使你停止等待,那条线也会旋转10秒.这太糟糕了,所以我希望你真的有一个很好的理由想要这个。
发布于 2021-02-14 02:26:57
像这样的事情应该有效:
fun main() {
println("start")
foo()
println("foo finished")
while (true);
}
fun foo() {
val start = System.currentTimeMillis()
GlobalScope.launch {
val endTimeMillis = System.currentTimeMillis() + (10 * 1000)
while (System.currentTimeMillis() <= endTimeMillis); //blocks for 10 seconds
println("${start.secondsSince()} job ends (10 seconds passed)")
}
println("${start.secondsSince()} waiting")
runBlocking {
delay(5000)
}
println("${start.secondsSince()} finished waiting")
}
fun Long.secondsSince() = (System.currentTimeMillis() - this) / 1000.00这将产生以下结果:
start
0.04 waiting
5.049 finished waiting
foo finished
10.043 job ends (10 seconds passed)不要理会计算秒数的可怕方法,现在是凌晨2点,但它确实证明了这一点。
现在来解释一下为什么会起作用。首先,我建议阅读这个所以质疑异步和启动之间的区别。TLDR launch用于触发和忘记,因为您对任何结果都不感兴趣(不需要返回值),因此不需要使用async。其次,延迟上的文档。
你的不起作用的原因是(据我所知)
job.await上使用了job.await,这与在协同服务本身上运行它并不完全相同。foo的乐趣是阻塞的,这意味着它将总是等待所有的协同线从里面启动,然后才会继续。这意味着您永远不会在foo execution finished之前获得job ends (10 seconds passed)。顺便说一句,实际的最终代码应该是:
fun foo() {
GlobalScope.launch {
//code that takes a long time
}
runBlocking { delay(5000) }
//other code that will continue after 5 seconds
}https://stackoverflow.com/questions/66191427
复制相似问题