如何将Java阻止功能转换为可取消的暂停功能?

时间:2019-11-13 08:05:26

标签: kotlin coroutine kotlin-coroutines

科特林suspend函数应按照惯例(1)进行非阻塞。通常,我们有依赖Java线程中断机制的旧Java代码,而我们不能(不想)modif(2):

public void doSomething(String arg) {
    for (int i = 0; i < 100_000; i++) {
        heavyCrunch(arg, i);
        if (Thread.interrupted()) {
            // We've been interrupted: no more crunching.
            return;
        }
    }
}

修改此代码以在协程中使用的最佳方法是什么?

版本A :这是不可接受的,因为它将在调用者线程上运行代码。因此将违反“暂停功能不会阻止调用者线程” 约定:

suspend fun doSomething(param: String) = delegate.performBlockingCode(param)

版本B :更好,因为它将在后台线程中运行阻止功能,因此它不会阻止调用者线程(除非偶然的情况是调用者使用了Dispatchers中的同一线程。线程池)。但是协程作业的取消不会中断依赖线程中断的performBlockingCode()。

suspend fun doSomething(param: String) = withContext(Dispatchers.Default) {
    delegate.performBlockingCode(param)
}

版本C :目前是我认为使其生效的唯一方法。这个想法是使用Java机制将阻塞函数转换为非阻塞,然后使用suspendCancellableCoroutine(3)将异步方法转换为暂停函数:

private ExecutorService executor = Executors.newSingleThreadExecutor();

public Future doSomethingAsync(String arg) {
    return executor.submit(() -> {
        doSomething(arg);
    });
}

suspend fun doSomething(param: String) = suspendCancellableCoroutine<Any> { cont ->
    try {
        val future = delegate.doSomethingAsync(param)
    } catch (e: InterruptedException) {
        throw CancellationException()
    }
    cont.invokeOnCancellation { future.cancel(true) }
}

如下所述,以上代码将无法正常工作,因为未调用continuation.resumeWith()

版本D :使用CompletableFuture:提供了一种在完成完成时注册回调的方法: thenAccept

private ExecutorService executor = Executors.newSingleThreadExecutor();

public CompletableFuture doSomethingAsync(String arg) {
    return CompletableFuture.runAsync(() -> doSomething(arg), executor);
}

suspend fun doSomething(param: String) = suspendCancellableCoroutine<Any> { cont ->
    try {
        val completableFuture = delegate.doSomethingAsync(param)
        completableFuture.thenAccept { cont.resumeWith(Result.success(it)) }
        cont.invokeOnCancellation { completableFuture.cancel(true) }
    } catch (e: InterruptedException) {
        throw CancellationException()
    }
}

您知道更好的方法吗?

  1. https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html
  2. https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761
  3. https://medium.com/@elizarov/callbacks-and-kotlin-flows-2b53aa2525cf

1 个答案:

答案 0 :(得分:1)

您可以通过suspend fun kotlinx.coroutines.runInterruptible

包装阻止代码

它禁止了编译警告,并且阻塞代码将在取消时抛出InterruptedException

val job = launch {
  runInterruptible {
    Thread.sleep(500)
  }
}

job.cancelAndJoin() // Cause will be 'java.lang.InterruptedException'

经过org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2

的测试