尽管调用了启动方法,但Kotlin协程还是同步运行

时间:2019-10-22 10:51:05

标签: kotlin kotlin-coroutines

我有一个雇员列表,我想为每个雇员打API。在同步模式下,这需要花费很多时间,我想使用协同程序来提高性能。这是我到目前为止所做的:

fun perform() = runBlocking {
    employeesSource.getEmployees()
            .map { launch { populateWithLeaveBalanceReports(it) } }
            .joinAll()
}

suspend fun populateWithLeaveBalanceReports(employee: EmployeeModel) {
        println("Start ${COUTNER}")
        val receivedReports = reportsSource.getReports(employee.employeeId) // call to a very slow API
        receivedReports { employee.addLeaveBalanceReport(it) }
        println("Finish ${COUTNER++}")
    }

当我尝试运行此代码时,代码正在同步运行,并且在控制台中,我看到以下输出:

Start 0
Finish 0
Start 1
Finish 1
Start 2
Finish 2

表示调用是按顺序进行的。如果我将populateWithLeaveBalanceReports函数中的整个代码替换为delay(1000),它将异步运行:

Start 0
Start 0
Start 0
Finish 0
Finish 1
Finish 2

我在做什么错?有什么想法吗?

2 个答案:

答案 0 :(得分:3)

协程不会神奇地将您的阻止网络API变为非阻止。使用launch(Dispatchers.IO) { ... }在弹性线程池中运行阻塞任务。只需注意,这并没有比普通的executorService.submit(blockingTask)做更多的事情。由于它使用了预先构建的全局线程池,因此更加方便。

答案 1 :(得分:0)

这些行可能正在使用阻塞代码-该代码依赖于阻塞线程来等待任务完成。

val receivedReports = reportsSource.getReports(employee.employeeId) receivedReports { employee.addLeaveBalanceReport(it) }

很可能您在reportsSource.getReports调用下使用了非异步http客户端或jdbc驱动程序。

如果是这样,您应该

  1. 重写reportsSource.getReports的代码,因此它不会依赖任何阻止代码。这是新的/非阻塞/具有挑战性的方式
  2. 使用threadPool执行程序手动分发执行,而不使用协程。这是旧的/简单的方法。