Kotlin:如何异步等待一系列相同的方法?

时间:2017-08-15 20:03:46

标签: kotlin

我有几百个Java类实例都需要完成他们的.calculate()方法或者在10分钟内死掉。他们会占用CPU和内存,所以我想一次只允许5(线程?)。我相信我很接近,但是来自Java背景我还不熟悉kotlin协同程序(vs java ExecutorServices)来进行编译。

fun MyClass.calculateTimeLimited(): Double = runBlocking {
  withTimeout(TIMEOUT) {
    this.calculate() // <-- doesn't compile! "this" is "CoroutineScope"

然后我想我需要用非阻塞计算来包装计算?或阻止,但超时有限?应该&#34; runBlocking&#34;在那儿?在上面的代码中作为lambda更好吗?

lapply

2 个答案:

答案 0 :(得分:3)

我不知道您是否知道,但有一个很棒的文件:coroutines by example。我链接了有关取消和超时的特定部分。以下是我对您的问题的实施:

import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.newSingleThreadContext
import java.util.*
import kotlin.system.measureNanoTime

internal val random = Random()

fun createRandArray(n: Long): Array<Int> {
    return createRandTArray(n).toTypedArray()
}

fun createRandTArray(n: Long): IntArray {
    return random.ints(n, 0, n.toInt()).toArray()
}

var size: Long = 1_000_000
var nRepeats: Int = 11
var nDrops: Int = 1

fun <T> benchmark(name: String,
                  buildSortable: (Long) -> T,
                  sort: (T) -> Any) {
    val arrays = List(nRepeats) { buildSortable(size) }
    val timesMS = arrays.map { measureNanoTime { sort(it) } / 1_000_000 }.drop(nDrops) // for JVM optimization warmup
    // println(timesMS)
    println("[$name] Average sort time for array of size $size: ${timesMS.average() } ms")
}

fun main(args: Array<String>) {
    size = 1_000_000
    nRepeats = 6

    benchmark("Array<Int>",
            buildSortable = { size -> createRandTArray(size).toTypedArray() },
            sort = ::mergeSort)

    benchmark("ListLike Array<Int>",
            buildSortable = { size -> SortingArray(createRandTArray(size).toTypedArray()) },
            sort = { array -> mergeSortLL(array) })  // oddly ::mergeSortLL is refused

    benchmark("ListLike IntArray",
            buildSortable = { size -> createRandTArray(size).asComparableList() },
            sort = { array -> mergeSortLL(array) })  // oddly ::mergeSortLL is refused

    benchmark("IntArray",
            buildSortable = { size -> createRandTArray(size) },
            sort = ::mergeSortIA)

    benchmark("IntArray multi-thread (CommonPool) with many array copies",
            buildSortable = { size -> createRandTArray(size) },
            sort = { mergeSortCorou(it) })

    val origContext = corouContext
    corouContext = newSingleThreadContext("single thread")
    benchmark("IntArray multi-thread (one thread!) with many array copies",
            buildSortable = { size -> createRandTArray(size) },
            sort = { mergeSortCorou(it) })
    corouContext = origContext

    benchmark("Java int[]",
            buildSortable = { size -> createRandTArray(size) },
            sort = { MergeSorter.sort(it) })
}

我得到了输出:

 Hit the timeout (CancellationException).
 Out of 100 computations, 45 completed.

您可以使用timeOut值(目前为500毫秒)。每个作业具有从0到1000毫秒的不同随机执行时间,因此大约一半的作业在超时之前执行。

但是,您可能更难以针对特定问题实施此操作。您的计算必须可取消。请仔细阅读我上面链接的文件中的取消部分。基本上你的计算要么必须调用suspend中的一个kotlinx.coroutines函数(这是我打电话delay后我做的),或者使用yield或{{1} }。

编辑:与取消任何作业的评论相关(不可取消/不可暂停):

不,这里没有魔力。无论您使用何种框架,使计算真正可取消都非常困难。众所周知,Java有isActive,它看起来像你想要的那样,但是deprecated

我尝试使用协程来解决在超时后停止提交新作业的简单问题,但是在超时之前启动的作业可以远远超出超时而不会被取消/中断。我花了一些时间在它上面,找不到一个简单的协程解决方案。它可以使用标准的Java并发结构来完成。

答案 1 :(得分:2)

我的看法(抱歉无法在我的机器上测试):

val results = streamOfInstances.asSeqence().map {
    async(CommonPool) {
        val errorRate: Double? = it?.calculate()
        println("${it?.javaClass?.simpleName}  $errorRate")
        errorRate
    }
}

runBlocking {
    results.forEach {
        println(it.await())
    }
}  

与您的代码的主要区别:

  • 我使用async代替launch,因为我正在收集结果
  • 屏蔽操作(join()await())位于runBlocking{}
  • 我使用的是Kotlin的map,而不是JDK8 API