kotlinx.coroutines.delay() 与 Thread.sleep()

81 篇文章 2 订阅
10 篇文章 0 订阅

kotlinx.coroutines.delay()是一个挂起函数。它不会阻塞当前线程。Thread.sleep()阻塞当前线程。Thread.sleep()这意味着该线程中的其他代码在退出之前不会执行。

示例 1 - kotlinx.coroutines.delay()

fun main(args: Array<String>) {
    runBlocking {
            run()
        }
    }
}

suspend fun run() {
    coroutineScope {
        val timeInMillis = measureTimeMillis {
            val mainJob = launch {
                //Job 0
                launch {
                    print("A->")
                    delay(1000)
                    print("B->")
                }
                //Job 1
                launch {
                    print("C->")
                    delay(2000)
                    print("D->")
                }
                //Job 2
                launch {
                    print("E->")
                    delay(500)
                    print("F->")
                }

                //Main job
                print("G->")
                delay(1500)
                print("H->")
            }

            mainJob.join()
        }

        val timeInSeconds =
            String.format("%.1f", timeInMillis/1000f)
        print("${timeInSeconds}s")
   }
}

Main Job将首先运行,然后将被delay()挂起函数挂起,然后是Job 0 -> Job 1 -> Job 2。所有Job都在大约同一时间暂停和启动。然后,最短的delay()将首先运行。timeinSeconds完成所有工作的时间最长应为delay()2 秒。

输出如下所示:

G->A->C->E->F->B->H->D->2.0s

这很容易理解。如果我们替换delay(2000)为Thread.Sleep(2000)for Job1怎么办?

示例 2 - Dispatchers.Main 上的 Thread.sleep()

suspend fun run() {
    coroutineScope {
        val timeInMillis = measureTimeMillis {
            val mainJob = launch {
                //Job 0
                launch {
                    print("A->")
                    delay(1000)
                    print("B->")
                }
                //Job 1
                launch {
                    print("C->")
                    Thread.sleep(2000)
                    print("D->")
                }
                //Job 2
                launch {
                    print("E->")
                    delay(500)
                    print("F->")
                }

                //Main job
                print("G->")
                delay(1500)
                print("H->")
            }

            mainJob.join()
        }

        val timeInSeconds =
            String.format("%.1f", timeInMillis/1000f)
        print("${timeInSeconds}s")
   }
}

与上面的示例 1 类似,MainJob将首先运行并由delay()suspend 函数挂起,然后是Job 0 → Job 1。 Job 0将被暂停。但是,当在Job 1Thread.sleep(2000)上运行时,线程将被阻塞 2 秒。此时Job 2未执行。

2 秒后,将首先打印出D ,然后是Job 2中的E。然后Job 2将被暂停。因为Main Job和Job 0被挂起不到 2 秒,它会立即运行。Job 0将首先运行,因为挂起时间更短。

0.5 秒后,Job 2恢复并完成。它将打印出F。

时间戳 1(0 秒后)

  • MainJob和Job 0启动和暂停。
  • Job 1启动并阻塞线程

时间戳 2(2 秒后)

  • Job 1已完成
  • Job 2已启动并暂停。
  • Job 0和MainJob恢复并完成。

时间戳 3(0.5 秒后)

  • Job 3恢复并完成

所以总时间消耗在 2.5 秒左右。

输出如下所示:

G->A->C->D->E->B->H->F->2.5s

示例 3 - Dispatchers.Default/IO 上的 Thread.sleep()

等等,如果在后台线程中使用or运行run挂起函数会怎样。例如:Dispatchers.DefaultDispatchers.IO

runBlocking {
    withContext(Dispatchers.Default) {
        run()
    }
}

输出变成这样:

A->C->G->E->F->B->H->D->2.0s

输出类似于上面的示例 1,其中Thread.sleep()似乎没有阻塞线程!为什么?

使用Dispatchers.Defaultor时Dispatchers.IO,它由线程池支持。每次我们调用时launch{},都会创建/使用不同的工作线程。

例如,这里是正在使用的工作线程:

  • Main Job - DefaultDispatcher-worker-1
  • Job 0 - DefaultDispatcher-worker-2
  • Job 1 - DefaultDispatcher-worker-3
  • Job 2 - DefaultDispatcher-worker-4

要查看当前正在运行的线程,您可以使用println(“Run ${Thread.currentThread().name}”)

所以Thread.sleep()确实阻塞了那个线程,但只阻塞了 DefaultDispatcher-worker-3. 其他Job仍然可以继续运行,因为它们在不同的线程上。

时间戳 1(0 秒后)

  • 启动Main Job、Job 0、Job 1和Job 2。顺序可以是随机的。见下文注(1)。
  • Main Job、Job 0和Job2被挂起。
  • Job 1阻塞了它自己的线程。

时间戳 2(0.5 秒后)

  • Job 2恢复并完成。

时间戳 3(1 秒后)

  • Job 0恢复并完成

时间戳 4(1.5 秒后)

  • 主要工作恢复完成

时间戳 5(2 秒后)

  • Job 1恢复并完成

因为每个Job都在不同的线程上运行,所以Job可以在不同的时间启动。所以 A、C、E、G 的输出可以是随机的。因此,您会看到 initiat Job的启动顺序与上面的示例 1 中的不同。

何时使用 Thread.Sleep()?

Thread.Sleep()几乎没用,因为大多数时候我们不想阻塞线程。kotlinx.coroutines.delay()被推荐。

我个人Thread.Sleep()用来模拟长时间运行的阻塞线程的任务。测试我是否已将长时间运行的任务放入后台线程很有用。如果我从主 UI 线程运行它,则 UI 将不会响应。

如果我simulateBlockingThreadTask()在主 UI 线程中调用它,它将阻塞主 UI 线程。应用程序将因 UI 无响应而崩溃。

private suspend fun simulateBlockingThreadTask() {
        Thread.sleep(2000)
 }

但是,如果我们使用 将线程切换到后台线程kotlinx.coroutines.withContext(),则从主 UI 线程调用它simulateBlockingThreadTask()不会使应用程序崩溃。

private suspend fun simulateBlockingThreadTask() {
    withContext(Dispatchers.Default) {
        Thread.sleep(2000)
    }
}

使用 yield()

通常最好不要长时间阻塞 UI 线程。

在代码示例中,我模拟了阻塞和非阻塞线程任务。总运行时间为 400 毫秒。

private suspend fun simulateLongRunningTask() {
    simulateBlockingThreadTask()
    simulateNonBlockingThreadTask()
}

private suspend fun simulateBlockingThreadTask() {
    repeat(10) {
        Thread.sleep(20)
        yield()
    }
}

private suspend fun simulateNonBlockingThreadTask() {
    delay(200)
}

结论

Thread.sleep()阻塞线程并且kotlinx.coroutines.delay()不会。

我Thread.sleep()用来测试我是否已正确地将长时间运行的任务放入后台线程。除此之外,我想不出我们想要使用的任何理由Thread.sleep()。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值