我正在使用Netty库(GitHub的第4版)。它在Scala中运行良好,但我希望我的库能够使用延续传递样式进行异步等待。
传统上使用Netty你会做这样的事情(例如异步连接操作):
//client is a ClientBootstrap
val future:ChannelFuture = client.connect(remoteAddr);
future.addListener(new ChannelFutureListener {
def operationComplete (f:ChannelFuture) = {
//here goes the code that happens when the connection is made
}
})
如果你正在实现一个库(我是),那么你基本上有三个简单的选项,允许库的用户在建立连接后做东西:
我想做的是第四种选择;我没有将它包括在上面的计数中,因为它并不简单。
我想使用scala分隔的continuation来使库的使用有点像阻塞库,但它将在幕后无阻塞:
class MyLibraryClient {
def connect(remoteAddr:SocketAddress) = {
shift { retrn: (Unit => Unit) => {
val future:ChannelFuture = client.connect(remoteAddr);
future.addListener(new ChannelFutureListener {
def operationComplete(f:ChannelFuture) = {
retrn();
}
});
}
}
}
}
想象一下,其他读/写操作以相同的方式实现。这样做的目的是用户的代码看起来更像这样:
reset {
val conn = new MyLibraryClient();
conn.connect(new InetSocketAddress("127.0.0.1", 1337));
println("This will happen after the connection is finished");
}
换句话说,该程序看起来像一个简单的阻塞式程序,但在幕后不会有任何阻塞或线程。
我遇到的麻烦是我不完全理解分隔连续的输入是如何工作的。当我尝试以上述方式实现它时,编译器抱怨我的operationComplete
实现实际上返回Unit @scala.util.continuations.cpsParam[Unit,Unit => Unit]
而不是Unit
。我得知scala的CPS中有一些“陷阱”,你必须使用shift
注释@suspendable
方法的返回类型,它会在调用堆栈中传递到reset
,但是似乎没有任何方法可以与已经存在的没有分隔连续概念的Java库进行协调。
答案 0 :(得分:4)
当我开始时,我发现Scala's continuations的这个解释非常有帮助。特别要注意他解释shift[A, B, C]
和reset[B, C]
的部分。添加虚拟null
作为operationComplete
的最后一个语句应该会有所帮助。
顺便说一下,如果retrn()
内嵌reset
,你需要在另一个shift
内调用import scala.util.continuations._
import java.util.concurrent.Executors
object Test {
val execService = Executors.newFixedThreadPool(2)
def main(args: Array[String]): Unit = {
reset {
val conn = new MyLibraryClient();
conn.connect("127.0.0.1");
println("This will happen after the connection is finished");
}
println("Outside reset");
}
}
class ChannelFuture {
def addListener(listener: ChannelFutureListener): Unit = {
val future = this
Test.execService.submit(new Runnable {
def run(): Unit = {
listener.operationComplete(future)
}
})
}
}
trait ChannelFutureListener {
def operationComplete(f: ChannelFuture): Unit
}
class MyLibraryClient {
def connect(remoteAddr: String): Unit@cps[Unit] = {
shift {
retrn: (Unit => Unit) => {
val future: ChannelFuture = new ChannelFuture()
future.addListener(new ChannelFutureListener {
def operationComplete(f: ChannelFuture): Unit = {
println("operationComplete starts")
retrn();
null
}
});
}
}
}
}
。
编辑:这是一个工作示例
Outside reset
operationComplete starts
This will happen after the connection is finished
可能的输出:
{{1}}