首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Kotlin coroutines异步withTimeout,它在线程用完时停止阻塞。

Kotlin coroutines异步withTimeout,它在线程用完时停止阻塞。
EN

Stack Overflow用户
提问于 2021-02-14 00:48:16
回答 2查看 1.7K关注 0票数 0

我正在尝试做一个函数来触发一个不可能被取消的缓慢的操作。我要这个行动在有超时的协同线中运行。由于操作不能像前面提到的那样被取消,所以我需要函数在超时后返回,但是操作要保持在后台。

我一直试图获得工作的代码异步运行10秒的长操作,时间为5秒,因此函数应该在超时后返回,让main继续其工作,打印"foo执行完成“,最后再打印5秒后,缓慢的作业将打印”作业结束(10秒通过)“。

下面是代码:

代码语言:javascript
复制
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")
    }
}

然后产生以下结果:

代码语言:javascript
复制
program execution begins
start awaiting with 5 secs timeout
job ends (10 seconds passed)
out of time
foo execution finished

但这并不是我之前提到的情况下所需要的行为。我需要这样做才能使输出看起来像:

代码语言:javascript
复制
program execution begins
start awaiting with 5 secs timeout
out of time
foo execution finished
job ends (10 seconds passed)

除此之外,我不能在异步中使用任何类型的"kotlin-coroutines“函数来存档这种行为(好吧,配合取消),因为调用的代码将是与协同过程无关的用户代码,可能是用Java编写的。因此,用于阻塞异步块而不是示例中的延迟()的while循环。

提前感谢您的帮助!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-02-14 03:42:44

如果不能中断阻塞代码,则需要在另一个线程中运行它。否则,您的线程将没有任何机会处理超时。

此外,您还需要确保包含阻塞代码的Job不是等待的Job的子级。否则超时将取消阻塞Job,但它仍将旋转10秒,runBlocking将等待它完成。

做这两件事的最简单的方法是使用GlobalScope,如下所示:

代码语言:javascript
复制
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秒.这太糟糕了,所以我希望你真的有一个很好的理由想要这个。

票数 1
EN

Stack Overflow用户

发布于 2021-02-14 02:26:57

像这样的事情应该有效:

代码语言:javascript
复制
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

这将产生以下结果:

代码语言:javascript
复制
start
0.04  waiting
5.049  finished waiting
foo finished
10.043 job ends (10 seconds passed)

不要理会计算秒数的可怕方法,现在是凌晨2点,但它确实证明了这一点。

现在来解释一下为什么会起作用。首先,我建议阅读这个所以质疑异步和启动之间的区别。TLDR launch用于触发和忘记,因为您对任何结果都不感兴趣(不需要返回值),因此不需要使用async。其次,延迟上的文档。

你的不起作用的原因是(据我所知)

  1. 您在job.await上使用了job.await,这与在协同服务本身上运行它并不完全相同。
  2. foo的乐趣是阻塞的,这意味着它将总是等待所有的协同线从里面启动,然后才会继续。这意味着您永远不会在foo execution finished之前获得job ends (10 seconds passed)

顺便说一句,实际的最终代码应该是:

代码语言:javascript
复制
fun foo() {
    GlobalScope.launch {
       //code that takes a long time
    }
    
    runBlocking { delay(5000) }
    
    //other code that will continue after 5 seconds
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66191427

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档