我想在Scala 2.11中构建一个侦听套接字的简单服务器。它应该异步读取套接字中的数据,并将数据从RxScala传递给Observable。
我有一个Java ServerSocket
,应该使用阻塞的方法readData
从中读取数据。此方法以启动启动并运行直到整个程序停止:
val server = new ServerSocket(port)
def readData(server: ServerSocket): Unit = ???
当从套接字读取数据时,我发现了两种不同的方法来避免阻塞整个程序:
new Thread {
override def run(): Unit = {
readData(server)
}
}.start()
和
Future {
blocking {
readData(server)
}
}
因为Future中没有包含返回值,然后可以将其传递给其他任务,Future的唯一任务就是使计算无阻塞。所以我想知道这些方法之间是否存在更大的差异?看看Future的实现,看起来它也创建并运行带有给定块的Runnable。那么,如果一个人有一个没有结果的单一且永久/长期运行的任务,那么这些方法中的一个更好吗?
答案 0 :(得分:3)
如果一个人有很长或永远,那么这些方法中的一个更可取 运行没有结果的任务?
这两个示例的不同之处在于,前者为每个请求分配一个新线程,第二个示例隐式使用Scala的默认ExecutionContext
,后者由ForkJoinPool
支持,这是基本上是一个可以根据需要扩展/缩小的线程池。
需要记住一个线程不是免费的,它需要分配一个堆栈(根据操作系统而变化),需要进行系统调用来注册该线程等等(你可以在Why is creating a Thread said to be expensive?)中阅读更多内容。
一般来说,我选择后者"天真"使用Future
与blocking
一起使用全局ExecutionContext
的方法。最重要的是,我会对我的代码进行基准测试,以确保我对代码的行为方式感到满意,然后根据这些调查结果进行调整。
另一个需要注意的重要事项:使用线程或线程池来处理事件仍然不会使异步,您只需使用多个线程来处理阻塞同步IO。我不熟悉SocketServer API,但一般情况下,如果它公开了一个自然异步API,则根本不需要额外的线程。例如,看一下内置支持异步IO的Netty。
正如您已澄清该操作是readData
的单调用,并且只要应用程序处于活动状态,该操作就会运行,只需一个专用{{1这里会有更好的主意。
答案 1 :(得分:0)
异步I / O优于同步的主要(如果不是单一的)优点是少量线程可以支持大量连接。由于程序中的连接数很少(1),我建议使用专用线程。这使程序更简单,可维护,更高效。