Scala Future vs Thread 长时间 运行 任务没有结果

Scala Future vs Thread for a long running task without result

我想在 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)
  }
}

因为没有 return 包装在 Future 中的值可以传递给其他任务,所以 Future 的唯一任务是使计算成为非阻塞的。所以我想知道这些方法之间是否有更大的差异?查看 Future 的实现,看起来它还使用给定的块创建并运行一个 Runnable。那么,如果只有一项 forever/long 运行 任务没有结果,那么这些方法中的一种是否更可取?

So is one of these approaches preferable if one has a long or forever running task without a result?

这两个示例的不同之处在于前者为每个请求分配一个新线程,而第二个示例隐式使用 Scala 的默认值 ExecutionContext,它由 ForkJoinPool 支持,这基本上是一个可以根据需要扩展 up/down 的线程池。

需要记住线程不是免费的,它需要分配一个堆栈(根据OS而变化),需要进行系统调用来注册该线程,等(您可以在 Why is creating a Thread said to be expensive? 中阅读更多内容)。

通常,我会采用后一种 "naive" 方法,即使用 Futureblocking,后者利用全局 ExecutionContext。最重要的是,我会对我的代码进行基准测试 以确保我对代码的行为方式感到满意,然后根据这些发现进行调整。

另一个需要注意的重要事项:使用线程或线程池来处理事件仍然不会异步,你只是使用多线程来处理阻塞同步IO .我不熟悉 SocketServer API,但一般来说,如果它公开自然异步 API,则根本不需要额外的线程。例如,看看 Netty 内置了对异步 IO 的支持。


编辑

正如您所阐明的那样,该操作是 单次 调用 readData,并且只要应用程序处于活动状态,该操作就会运行,一个专用的Thread 在这里会是一个更好的主意。

异步 I/O 相对于同步的主要(如果不是唯一)优势是少量线程可以支持大量连接。由于您程序中的连接数很少(1),我建议使用专用线程。这使程序更简单、可维护且更高效。