Kotlin 使用协程并行执行作业

Kotlin 使用协程并行执行作业,kotlin,kotlin-coroutines,Kotlin,Kotlin Coroutines,我想使用协程并行执行多个作业。这是我想出的一段代码。 我有两个问题: 如何确保在调用线程中发生完成回调 代码变得更像我以前使用的回调模式 普通螺纹。请提出设计变更建议,以实现 协同程序具有可读性优势 类并行执行器{ 暂停乐趣执行( 作业:列表输出>, onTimeout:(作业索引:Int)->输出, onFailure:(作业索引:Int,异常:Throwable)->输出, onCompletion:suspend(作业索引:Int,结果:OUTPUT)->单位, 超时时间:长, onF

我想使用协程并行执行多个作业。这是我想出的一段代码。
我有两个问题:

  • 如何确保在调用线程中发生完成回调

  • 代码变得更像我以前使用的回调模式 普通螺纹。请提出设计变更建议,以实现 协同程序具有可读性优势

类并行执行器{ 暂停乐趣执行( 作业:列表输出>, onTimeout:(作业索引:Int)->输出, onFailure:(作业索引:Int,异常:Throwable)->输出, onCompletion:suspend(作业索引:Int,结果:OUTPUT)->单位, 超时时间:长, onFullCompletion:suspend()->Unit={}, invokeDispatcher:CoroutineDispatcher=Dispatchers.Default ) { withContext(invokeDispatcher){ 变量计数器=0 val listenJobs=mutableListOf() jobs.foreachinedexed{index,job-> val listenJob=async{ 试一试{ 工作() }捕获(e:例外){ onFailure(索引,e) } } 添加(listenJob) } listenJobs.forachined{索引,作业-> 发射{ val输出=try{ withTimeout(超时){ 工作等待 } }捕获(e:TimeoutCancellationException){ onTimeout(索引) } onCompletion(索引、输出) if(++计数器==listenJobs.size){ onFullCompletion() } } } } } }
做了一些改进,可以回答您的问题


class ParallelExecutor {

    suspend fun <OUTPUT> execute(
        jobs: List<suspend () -> OUTPUT>,
        onTimeout: (jobIndex: Int) -> OUTPUT,
        onFailure: (jobIndex: Int, exception: Throwable) -> OUTPUT,
        onCompletion: suspend (jobIndex: Int, result: OUTPUT) -> Unit,
        timeout: Long,
        invokeDispatcher: CoroutineDispatcher = Dispatchers.Default
    ) {
        supervisorScope {
            val listenJobs = jobs.map { job ->
                async(invokeDispatcher) {
                    withTimeout(timeout) {
                        job()
                    }
                }
            }

            listenJobs.forEachIndexed { index, job ->
                launch {
                    val output = try {
                        job.await()
                    } catch (e: TimeoutCancellationException) {
                        onTimeout(index)
                    } catch (e: Exception) {
                        onFailure(index, e)
                    }
                    onCompletion(index, output)
                }
            }
        }
    }
}


类并行执行器{
暂停乐趣执行(
作业:列表输出>,
onTimeout:(作业索引:Int)->输出,
onFailure:(作业索引:Int,异常:Throwable)->输出,
onCompletion:suspend(作业索引:Int,结果:OUTPUT)->单位,
超时时间:长,
invokeDispatcher:CoroutineDispatcher=Dispatchers.Default
) {
监测仪{
val listenJobs=jobs.map{job->
异步(invokeDispatcher){
withTimeout(超时){
工作()
}
}
}
listenJobs.forachined{索引,作业->
发射{
val输出=try{
工作等待
}捕获(e:TimeoutCancellationException){
onTimeout(索引)
}捕获(e:例外){
onFailure(索引,e)
}
onCompletion(索引、输出)
}
}
}
}
}
  • 作业现在在达到超时时被取消
  • 完成回调现在在调用方的调度程序中调用
  • 决定何时调用
    onFullCompletion
    时的争用条件已修复
  • 删除了一些您并不真正需要的方法

如果您觉得这更像是回调模式,那么就不应该使用回调。协同程序的设计使您可以在使用站点用最少的样板文件编写此类代码,因此这样的函数是不必要的,而且看起来很奇怪(IMHO)。

在我看来,您可以大大简化代码。您不需要两步式习惯用法,即首先启动所有
async
作业,然后启动更多作业等待它们。您只需
启动
作业并将其委托给同一块中的回调。这样,回调自然会在调用方的调度程序上调用,并且只有作业本身可以在更改的上下文中使用
invokeDispatcher
调用

onFullCompletion
看起来像是一段属于调用方的代码,在
execute
调用下面。由于
execute
不会抛出任何异常,因此您不需要任何
try finally
来获取它

suspend fun <OUTPUT> execute(
    jobs: List<suspend () -> OUTPUT>,
    onTimeout: (jobIndex: Int) -> OUTPUT,
    onFailure: (jobIndex: Int, exception: Throwable) -> OUTPUT,
    onCompletion: suspend (jobIndex: Int, result: OUTPUT) -> Unit,
    timeout: Long,
    invokeDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
    coroutineScope {
        jobs.mapIndexed { index, job ->
            launch {
                val output = try {
                    withTimeout(timeout) {
                        withContext(invokeDispatcher) {
                            job()
                        }
                    }
                } catch (e: TimeoutCancellationException) {
                    onTimeout(index)
                } catch (e: Exception) {
                    onFailure(index, e)
                }
                onCompletion(index, output)
            }
        }
    }
}
suspend-fun-execute(
作业:列表输出>,
onTimeout:(作业索引:Int)->输出,
onFailure:(作业索引:Int,异常:Throwable)->输出,
onCompletion:suspend(作业索引:Int,结果:OUTPUT)->单位,
超时时间:长,
invokeDispatcher:CoroutineDispatcher=Dispatchers.Default
) {
共线镜{
jobs.mapIndexed{index,job->
发射{
val输出=try{
withTimeout(超时){
withContext(invokeDispatcher){
工作()
}
}
}捕获(e:TimeoutCancellationException){
onTimeout(索引)
}捕获(e:例外){
onFailure(索引,e)
}
onCompletion(索引、输出)
}
}
}
}

这是一个非常。。。使用协同程序的不同方式,您确定要使用协同程序来实现吗?我不认为这需要这样的泛型。这个函数在你的代码中是如何使用的?这将使提出建议更容易。您希望在“调用方线程”或“调用方调度程序”中调用完成回调吗?(线程可能不可能)。@DominicFischer我没有几个地方可以同时启动多个相同的作业。这段代码在所有这些地方都是多余的。所以我把它搬到外面去了。我就是这样做到的。谢谢。我实际上想要onFullCompletion回调。因此,消费者不必决定所有工作何时全部完成。它们可以将清理逻辑作为参数传递。我很担心
suspend fun <OUTPUT> execute(
    jobs: List<suspend () -> OUTPUT>,
    onTimeout: (jobIndex: Int) -> OUTPUT,
    onFailure: (jobIndex: Int, exception: Throwable) -> OUTPUT,
    onCompletion: suspend (jobIndex: Int, result: OUTPUT) -> Unit,
    timeout: Long,
    invokeDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
    coroutineScope {
        jobs.mapIndexed { index, job ->
            launch {
                val output = try {
                    withTimeout(timeout) {
                        withContext(invokeDispatcher) {
                            job()
                        }
                    }
                } catch (e: TimeoutCancellationException) {
                    onTimeout(index)
                } catch (e: Exception) {
                    onFailure(index, e)
                }
                onCompletion(index, output)
            }
        }
    }
}