我已经有两个长期运行的任务,每个任务都封装在withContext中。但是现在我想并行运行它们,等待它们的结果。因此,我想知道我是否像下面这样使用它们(用异步封装它们):
val result1 = async {
withContext(Dispatcher.IO) {
someLongRunningBackgroundWork1...
....
}
}
val 2 = withContext(Dispatcher.IO) {
someLongRunningBackgroundWork2...
....
}
}这个很好用。但我想知道这场戏背后有没有什么取舍?
发布于 2022-02-27 02:48:26
async(Dispatchers.IO) { /*...*/ }是与async { withContext(Dispatchers.IO) { /*...*/} }相同的逻辑。在任何一种情况下,不明确的异常都会导致async失败。如果在coroutine中使用这样的裸async调用,那么它就是一个子协同,异常将被传播到父进程。如果在特定的async上调用了CoroutineScope,那么它不是子协同器,除非调用await()或join(),否则不会传播它的异常。通常,当您希望在协同线中运行一些并行任务,然后继续运行时,可以将它们封装在一个coroutineScope调用中。
你可以在你的协同线上做这样的事情来分解平行的工作。在coroutineScope内部抛出的任何不正常异常都会在其外部重新抛出,并将取消coroutineScope中任何未运行的子协同。
val (result1, result2) = coroutineScope {
val result1 = async(Dispatcher.IO) {
//someLongRunningBackgroundWork1...
//....
}
val result2 = withContext(Dispatcher.IO) {
//someLongRunningBackgroundWork2...
//....
}
result1.await() to result2
}与现在相比,它的优点是,如果任何一个代码块失败,其他并行任务将自动被取消。
发布于 2022-02-27 06:36:31
正如您在注释中所说的,您的函数使用withContext(IO),您不想改变这一点。将它们封装在async中将首先启动当前调度程序中的协同线(如果您使用的是Main),然后立即切换到IO。虽然不会花很多钱,但还是没有必要。如果您将函数包装在async(IO)中,协同线将直接在正确的调度程序上启动,而内部的withContext(IO) { }基本上是不操作的。
正如Tenfour04提出的,启动背景工作并等待其结果应该包含在一个单一的工作单元中,一旦其任何部分失败,该单元就会立即失败。您不会显示在已启动的协同线上调用await()的位置,但通常async和await语句都应该发生在同一个coroutineScope块中。当该块结束时,您可以保证在后台不再有正在进行的工作,如果任何其他并发子任务(或主要任务)失败,还可以通过取消它来节省时间和资源。
https://stackoverflow.com/questions/71273107
复制相似问题