【问题标题】:Lightweight threads in AkkaAkka 中的轻量级线程
【发布时间】:2014-05-01 19:34:06
【问题描述】:

我最近读到了Quasar,它为 JVM 提供了“轻量级”/类似 Go 的“用户模式”线程(它也有一个受 Erlang 启发的 Actor 系统,比如 Akka,但这不是主要问题)

例如:

package jmodern;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.strands.Strand;
import co.paralleluniverse.strands.channels.Channel;
import co.paralleluniverse.strands.channels.Channels;

public class Main {
    public static void main(String[] args) throws Exception {
        final Channel<Integer> ch = Channels.newChannel(0);

        new Fiber<Void>(() -> {
            for (int i = 0; i < 10; i++) {
                Strand.sleep(100);
                ch.send(i);
            }
            ch.close();
        }).start();

        new Fiber<Void>(() -> {
            Integer x;
            while((x = ch.receive()) != null)
                System.out.println("--> " + x);
        }).start().join(); // join waits for this fiber to finish
    }
}

据我了解,上面的代码不会产生任何 JVM/内核线程,所有这些都是在用户模式线程中完成的(或者他们声称),这应该更便宜(如果我理解的话,就像 Go 协程一样正确)

我的问题是这个 - 据我了解,在 Akka 中,一切仍然基于 JVM 线程,大部分时间映射到本机 OS 内核线程(例如 POSIX 系统中的 pthreads),例如据我所知,Akka 中没有用户模式线程/像协同程序/轻量级线程一样,我理解正确吗?

如果是这样,那么您知道这是否是一种设计选择吗?或者未来在 Akka 中是否有类似 go 的轻量级线程的计划?

我的理解是,如果你有一百万个 Actor,但其中大多数是阻塞的,那么 Akka 可以用更少的物理线程来处理它,但如果它们中的大多数是非阻塞的,你仍然需要系统的一些响应(例如服务百万小请求流一些数据)然后我可以看到用户模式线程实现的好处,它可以允许更多的“线程”以较低的创建切换和终止的成本存活(当然唯一的好处是均匀为许多客户划分响应能力,但响应能力仍然很重要)

我的理解或多或少是正确的吗?如果我错了,请纠正我。

*我可能完全将用户模式线程与 go/co-routines 和轻量级线程混淆了,上面的问题依赖于我对它们都是相同的理解不足。

【问题讨论】:

  • 这是我在使用 Python 时所记得的。这不是一个直接的答案,但它可能有用。用户/轻量级线程和实际系统线程都有其用途。归根结底,两者都提供并发性,但只有系统线程提供并行性。因此,如果您正在进行大量计算,您将不会从使用用户线程中获得任何提升,但您会从系统线程中获得任何提升。在另一种情况下,您只有一个 CPU 内核并且您想要一台服务器,使用用户线程是唯一的解决方案。
  • 是的,这也是我所理解的,例如用户线程仅适用于提供 CPU 的“良好利用率”,例如在有时会阻塞 IO 的 2 个用户线程之间切换比使用完整线程便宜。你没有得到并行性,但如果我理解正确,你确实可以更好地利用 CPU
  • 请看我的回答,它会有所帮助:stackoverflow.com/questions/29680718/…

标签: java multithreading scala akka quasar


【解决方案1】:

Akka 是一个非常灵活的库,它允许您使用简单的 trait ExecutionContext 来安排 actor(本质上是通过一系列 trait 来实现),如您所见,它接受 Runnables 和以某种方式执行它们。因此,据我所知,很可能可以编写与 Quasar 之类的绑定并将其用作 Akka 演员的“后端”。

但是,Quasar 和类似的库可能会为光纤之间的通信提供特殊实用程序。我也不知道他们将如何处理像 I/O 这样的阻塞任务,可能他们也会有这样的机制。因此,我不确定 Akka 是否能够在绿色线程上正确运行。 Quasar 似乎也依赖于字节码检测,这是一种相当先进的技术,它可能会阻止它支持 Akka。

但是,在使用 Akka Actor 时,您不必担心线程的轻量性。事实上,Akka 完全能够在单个系统上创建数百万个 Actor(请参阅 here),并且所有这些 Actor 都可以正常工作。

这是通过对特殊类型的线程池(例如 fork-join 线程池)进行巧妙调度来实现的。这意味着除非actor在一些长时间运行的计算上被阻塞,否则它们可以运行的线程数量明显少于这些actor的数量。例如,您可以创建一个最多使用 8 个线程的线程池(8 核处理器的每个内核一个),所有 Actor 活动都将安排在这些线程上。

如果需要,Akka 的灵活性允许您配置精确的调度程序以供特定参与者使用。您可以为长期运行的任务(如数据库访问)的参与者创建专用调度程序。请参阅here 了解更多信息。

所以,简而言之,不,您不需要演员的用户态线程,因为演员不会一对一映射到本机线程(除非您强制他们这样做,也就是说,但应不惜一切代价避免这种情况)。

【讨论】:

  • 更少的线程——在相同延迟的情况下更少的上下文切换。所以,简而言之,是的,如果可以的话,你可以考虑使用用户态线程。
  • “这是通过对特殊类型的线程池进行巧妙调度来实现的”
  • @Franz 我不确定最简单的形式,但您可以从 akka 文档中的 about dispatchers 开始阅读,然后直接继续阅读 fork-join thread pool 的文档。你也可以google一下fork-join线程池,你会发现很多教程和解释。
【解决方案2】:

Akka actor 本质上是异步,这就是为什么你可以拥有很多它们,而非常接近 Erlang 的 Quasar actor(是的,Quasar 也提供了一个 actor 实现)是 同步阻塞,但它们使用纤程而不是Java(即目前的操作系统)线程,因此您仍然可以拥有许多具有与异步相同的性能但只是编程风格的线程就像使用常规线程和阻塞调用一样简单。

I/O 通过集成处理:Quasar includes a framework to convert both async and sync APIs into fiber-blockingComsat 已经包含许多此类集成(其中一个带有 Java NIO 的集成直接包含在 Quasar 中)。

My reply to another question 包含更多信息。

【讨论】:

    猜你喜欢
    • 2017-05-06
    • 1970-01-01
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    相关资源
    最近更新 更多