我想使用Kotlin(v1.3.0)协程和java.nio.channels。 SocketChannel (NIO)替换Socket connect
(阻止IO )。因为这样可以节省许多线程。
以下代码无法运行,因为job.await()
是Kotlin中的暂停函数,只能在Ktolin协程块中调用。像launch{..}
,async{..}
。
// this function will be called by Java Code
fun connect(address: InetSocketAddress, connectTimeout: Int): SocketChannel {
// Start a new connection
// Create a non-blocking socket channel
val socketChannel = SocketChannel.open()
socketChannel.configureBlocking(false)
// async calls NIO connect function
val job = GlobalScope.async(oneThreadCtx) {
aConnect(socketChannel, address)
}
// I what to suspend(NOT block) current Java Thread, until connect is success
job.await()
return socketChannel
}
但是,我尝试使用runBlocking{..}
将此功能设置为Java中的普通功能。但job.await
阻止了当前的Java线程未暂停。
那么,如何使用Kotlin(v1.3.0)协程实现此功能?
答案 0 :(得分:2)
正如Marko所指出的那样,即使该阻塞操作在异步协程中,您的代码仍将最终阻塞一个线程。要真正获得Java和Kotlin所需的异步行为,您需要使用Socket Channel
的异步版本这样,您将获得真正的异步套接字处理。使用该类和Kotlin的suspendCoroutine构建器方法,您可以将异步处理程序转换为可挂起的调用。
这里是实现阅读的示例:
class TcpSocket(private val socket: AsynchronousSocketChannel) {
suspend fun read(buffer: ByteBuffer): Int {
return socket.asyncRead(buffer)
}
fun close() {
socket.close()
}
private suspend fun AsynchronousSocketChannel.asyncRead(buffer: ByteBuffer): Int {
return suspendCoroutine { continuation ->
this.read(buffer, continuation, ReadCompletionHandler)
}
}
object ReadCompletionHandler : CompletionHandler<Int, Continuation<Int>> {
override fun completed(result: Int, attachment: Continuation<Int>) {
attachment.resume(result)
}
override fun failed(exc: Throwable, attachment: Continuation<Int>) {
attachment.resumeWithException(exc)
}
}
}
您可以选择删除我在这里进行的包装,而只在AsynchronousSocketChannel上公开一个asyncRead
方法,如下所示:
suspend fun AsynchronousSocketChannel.asyncRead(buffer: ByteBuffer): Int {
return suspendCoroutine { continuation ->
this.read(buffer, continuation, ReadCompletionHandler)
}
}
object ReadCompletionHandler : CompletionHandler<Int, Continuation<Int>> {
override fun completed(result: Int, attachment: Continuation<Int>) {
attachment.resume(result)
}
override fun failed(exc: Throwable, attachment: Continuation<Int>) {
attachment.resumeWithException(exc)
}
}
这完全取决于口味以及您的设计目标是什么。您应该能够为初次连接实现类似的方法,就像我在这里阅读的那样。
答案 1 :(得分:0)
// I what to suspend(NOT block) current Java Thread, until connect is success
job.await()
这不是现实的期望。从Java的角度来看,suspend fun
通过返回特殊常量COROUTINE_SUSPENDED
暂停执行。您需要Kotlin编译器将其隐藏起来,并允许您编写可挂起的常规代码。
即使从Kotlin的角度来看,您的代码也没有非阻塞挂起,因为它使用阻塞调用进行连接。将该调用提交到另一个线程并不会使它成为非阻塞的。
您的代码所做的工作完全等同于将作业提交给Java执行服务,然后等待其结果。例如,您可以使用CompletableFuture.supplyAsync
。