Coroutine未并行运行求助:WorkManager中挂起函数串行执行
嘿,我来帮你分析下为啥你的代码会串行执行,以及怎么改成并行的~
首先,你的问题出在协程的启动和等待逻辑没处理对。你现在的callX()函数里虽然用了async,但这些async创建的协程并没有被正确地并行启动和等待,反而因为调用方式的问题,导致任务被串行执行了。
问题根源
你当前的callX()函数里,withContext(Dispatchers.IO)只是切换到IO线程创建了一个async协程,但这个async并没有被等待——理论上call1()应该立即返回一个Deferred对象,不会阻塞后续的call2()和call3()。但你看到的串行日志,大概率是因为你没有对这些Deferred做任何等待处理,加上callX()的写法有点冗余,导致实际运行中协程没有被正确并行调度。
正确的并行实现方式
我们只需要调整协程的启动逻辑,让三个任务真正并行起来:
第一步:简化你的请求函数
先把callX()改成普通的suspend函数,负责切换到IO线程并执行请求逻辑(去掉内部多余的async):
suspend fun call1() = withContext(Dispatchers.IO) { Log.d(TAG,"Call 1 started") delay(2000) // 这里替换成你的实际网络请求 Log.d(TAG,"Call 1 finished") } suspend fun call2() = withContext(Dispatchers.IO) { Log.d(TAG,"Call 2 started") delay(5000) Log.d(TAG,"Call 2 finished") } suspend fun call3() = withContext(Dispatchers.IO) { Log.d(TAG,"Call 3 started") delay(1000) Log.d(TAG,"Call 3 finished") }
第二步:在doWork中并行启动并等待所有任务
在doWork()里,用coroutineScope创建协程作用域,然后通过async并行启动三个请求,最后用awaitAll()等待所有任务完成:
override suspend fun doWork(): Result{ setForeground(createForegroundInfo()) // 创建协程作用域,确保所有子协程完成后才继续执行 coroutineScope { // 并行启动三个任务 val task1 = async { call1() } val task2 = async { call2() } val task3 = async { call3() } // 等待所有任务完成 listOf(task1, task2, task3).awaitAll() } return Result.success() }
为什么这样能并行?
coroutineScope:它会创建一个协程作用域,所有在这个作用域内启动的子协程都会被管理——如果任何一个子协程失败,整个作用域会取消其他所有子协程,这符合WorkManager的工作逻辑(要么全部完成,要么取消)。async:每个async都会启动一个独立的协程,这些协程会在Dispatchers.IO线程池里并行执行,不会互相阻塞。awaitAll():会等待所有async返回的Deferred对象完成,确保所有网络请求都执行完毕后,才返回Result.success()。
另一种可选写法(保留内部async)
如果你坚持要在callX()里用async,那需要收集所有Deferred对象并等待它们完成:
suspend fun call1(): Deferred<Unit> = withContext(Dispatchers.IO) { async{ Log.d(TAG,"Call 1 started") delay(2000) Log.d(TAG,"Call 1 finished") } } // call2、call3同理 override suspend fun doWork(): Result{ setForeground(createForegroundInfo()) coroutineScope { val allTasks = listOf(call1(), call2(), call3()) allTasks.awaitAll() } return Result.success() }
不过这种写法有点冗余,因为callX()本身已经是suspend函数,没必要再嵌套async,推荐用第一种写法。
这样修改后,你就能看到预期的日志顺序:三个started几乎同时打印,然后Call 3 finished先出现,接着是Call 1 finished,最后是Call 2 finished。
内容的提问来源于stack exchange,提问作者tyczj

