Java 番石榴事件巴士调度

Java 番石榴事件巴士调度,java,guava,Java,Guava,我正在使用Guava的EventBus启动一些处理和报告结果。下面是一个非常简单的可编译示例: import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class Test { public static class InitiateProcessing { } public static class ProcessingStarted {

我正在使用Guava的EventBus启动一些处理和报告结果。下面是一个非常简单的可编译示例:

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

public class Test {

    public static class InitiateProcessing { }
    public static class ProcessingStarted { }
    public static class ProcessingResults { }
    public static class ProcessingFinished { }

    public static EventBus bus = new EventBus();

    @Subscribe
    public void receiveStartRequest(InitiateProcessing evt) {
        System.out.println("Got processing request - starting processing");
        bus.post(new ProcessingStarted());

        System.out.println("Generating results");
        bus.post(new ProcessingResults());
        System.out.println("Generating more results");
        bus.post(new ProcessingResults());

        bus.post(new ProcessingFinished());
    }

    @Subscribe
    public void processingStarted(ProcessingStarted evt) {
        System.out.println("Processing has started");
    }

    @Subscribe
    public void resultsReceived(ProcessingResults evt) {
        System.out.println("got results");
    }

    @Subscribe
    public void processingComplete(ProcessingFinished evt) {
        System.out.println("Processing has completed");
    }


    public static void main(String[] args) {
        Test t = new Test();
        bus.register(t);
        bus.post(new InitiateProcessing());
    }
}
我使用这些事件作为其他软件组件的一种反应方式,以准备此处理。例如,他们可能必须在处理前保存当前状态,然后在处理后恢复

我预计该计划的产出为:

Got processing request - starting processing
Processing has started
Generating results
got results
Generating more results
got results
Processing has completed
相反,实际输出为:

Got processing request - starting processing
Generating results
Generating more results
Processing has started
got results
got results
Processing has completed
本应指示处理已开始的事件实际上发生在实际处理之后(“生成结果”)

看了源代码之后,我明白了为什么它会这样。下面是与
EventBus
相关的

  /**
   * Drain the queue of events to be dispatched. As the queue is being drained,
   * new events may be posted to the end of the queue.
   */
  void dispatchQueuedEvents() {
    // don't dispatch if we're already dispatching, that would allow reentrancy
    // and out-of-order events. Instead, leave the events to be dispatched
    // after the in-progress dispatch is complete.
    if (isDispatching.get()) {
        return;
    }
    // dispatch event (omitted)
发生的事情是,因为我已经在调度顶级的
InitiateProcessing
事件,其余的事件就被推到了队列的末尾。我希望它的行为类似于.NET事件,在所有处理程序完成之前,调用事件不会返回

我不太明白这种实现的原因。当然,事件保证是有序的,但是周围代码的顺序完全被扭曲了

有没有办法让总线按照描述的方式运行并产生所需的输出?我确实在Javadocs中读到

EventBus保证它不会从中调用订户方法 同时多个线程,除非该方法明确允许 它是通过带有@AllowConcurrentEvents注释来实现的

但我认为这不适用于这里——我在单线程应用程序中看到了这个问题

编辑


这里出现问题的原因是我从订阅者内部发布。因为事件总线是不可重入的,所以这些“子帖子”将排队,并在第一个处理程序完成后处理。我可以注释掉
EventBus
源代码中的
if(isDispatching.get()){return;}
部分,一切都按照我的预期进行-所以真正的问题是我这样做会引入哪些潜在问题?看来设计师们做出了一个认真的决定,不允许重入

在发送到EventBus时,直到所有“订阅者”都收到信号后才会返回。。这些订阅者可能还没有开始执行。这意味着,当第一个bus.post返回时,您将继续下一个post,而不会有任何中间订户开始处理

public void post(对象事件)将事件发布到所有已注册的 订户。事件结束后,此方法将成功返回 已发布给所有订阅者,且不考虑任何例外情况 由订阅者抛出。如果尚未订阅任何订阅服务器 事件的类,并且事件还不是DeadEvent,它将是 包装在DeadEvent中并重新发布

参数:事件-要发布的事件


EventBus
通常的工作原理是,将事件发布到总线的代码不应关心订阅者对事件的处理方式或时间,而只考虑事件发布的顺序(在同步事件总线的情况下)


如果您希望在方法过程中的特定时间调用特定方法,并且希望确保这些方法在方法继续之前完成(就像您在示例中看到的那样),为什么不直接调用这些方法呢?当您使用事件总线时,您将显式地将代码与响应给定事件所发生的事情分开。这在许多情况下都是可取的,也是存在事件总线的主要原因,但这似乎不是您想要的。

我试图总结番石榴的事件总线事件交付行为:

如果在时刻t1发布事件E1,则通知所有订户。 如果其中一个订阅者在其@Subscribe方法中发布了一个事件本身(稍晚一点),则“新”事件E2将排队并在之后传递。之后这里的意思是:在所有来自t1的E1的@Subscribe方法返回之后

将这种“级联”事件发布与广度优先树遍历进行比较


这似乎是EventBus的明确选择设计。

我知道这个问题已经讨论了4年,但今天我遇到了同样的问题。有一个简单的(和直觉相反的)改变来获得你想要的行为。根据,您可以将AsyncEventBus与DirectExecutor一起使用:

public static EventBus bus = new AsyncEventBus(MoreExecutors.newDirectExecutorService());
使用上述更改运行测试代码,结果正是您想要的:

Got processing request - starting processing
Processing has started
Generating results
got results
Generating more results
got results
Processing has completed

看起来事件总线在它自己的线程中运行。这通常意味着,操作是异步执行的,并且(只要它是一个总线)保证按照它的顺序交付,而它与主节点无关thread@injecteer它不运行自己的线程。它们确实有一个
AsyncEventBus
,允许您指定一个
执行器,但我不使用它。这都是单线程的。你可能是对的。虽然我认为,它们是在一个新线程中运行的:)你能通过在每个处理
@Subscribe
-d方法中添加
System.out.println(“curr-thread:+thread.currentThread().getName())
来测试它吗?为什么你从订阅服务器中开始.post()?这里有些可疑,这正是我看到这个问题的原因。这不是一个有效的用例吗?从概念上讲,事件可以触发其他事件——我们一直在假设这没问题的情况下操作。这并不完全正确——在这种情况下,当第一个post返回所有事件时,所有事件都已处理。不管是哪种方式,这都不能回答我关于是否可以让总线按预期的方式运行的问题。我想要的是将我的代码与响应给定事件的实际情况分开。我想让活动说“为即将发生的事情做好准备”——我不在乎订阅者如何或怎样做才能做好准备。他们可能什么都不用做。我只是