Guava EventBus dispatching
我正在使用 Guava 的 EventBus 启动一些处理并报告结果。这是一个非常简单的可编译示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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()); } } |
我使用这些事件作为其他软件组件做出反应以准备此处理的一种方式。例如,他们可能必须在处理之前保存其当前状态并在之后恢复它。
我希望这个程序的输出是:
1 2 3 4 5 6 7 | Got processing request - starting processing Processing has started Generating results got results Generating more results got results Processing has completed |
相反,实际的输出是:
1 2 3 4 5 6 7 | Got processing request - starting processing Generating results Generating more results Processing has started got results got results Processing has completed |
应该表明处理已经开始的事件实际上发生在实际处理之后("生成结果")。
查看源代码后,我明白为什么会这样。这是
的相关源代码
1 2 3 4 5 6 7 8 9 10 11 12 | /** * 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) |
发生的事情是因为我已经在调度顶级
我不太明白这种实现的原因。当然,可以保证事件是有序的,但是周围代码的顺序会完全扭曲。
有什么方法可以让总线按照描述的方式运行并产生所需的输出?我确实在 Javadocs 中读到了
The EventBus guarantees that it will not call a subscriber method from
multiple threads simultaneously, unless the method explicitly allows
it by bearing the @AllowConcurrentEvents annotation.
但我认为这不适用于这里 - 我在单线程应用程序中看到了这个问题。
编辑
这里问题的原因是我
我尝试总结一下 Guava 的 EventBus 事件传递行为:
如果在 t1 时刻发布了事件 E1,则通知所有订阅者。
如果其中一个订阅者在它的@Subscribe-method 中发布了一个事件本身(片刻之后),则"新"事件 E2 将被排入队列并在之后交付。之后的意思是:毕竟@Subscribe-methods for E1 from t1 确实返回了。
将这种"级联"事件发布与广度优先树遍历进行比较。
这似乎是 EventBus 的明确选择设计。
如果您希望在您的方法过程中的特定时间调用特定方法,并且您希望确保这些方法在您的方法继续之前完成(就像您在示例中看到的那样),为什么不直接调用这些方法呢?当您使用事件总线时,您明确地将您的代码与响应给定事件的确切情况分开。这在许多情况下是可取的,也是
我知道这个问题已经有 4 年了,但我今天遇到了同样的问题。有一个简单的(和违反直觉的)改变来获得你想要的行为。根据 https://stackoverflow.com/a/53136251/1296767,您可以将 AsyncEventBus 与 DirectExecutor 一起使用:
1 | public static EventBus bus = new AsyncEventBus(MoreExecutors.newDirectExecutorService()); |
使用上述更改运行您的测试代码,结果正是您想要的:
1 2 3 4 5 6 7 | Got processing request - starting processing Processing has started Generating results got results Generating more results got results Processing has completed |
在通知所有"订阅者"之前,不会返回到 EventBus 的发布......这些订阅者可能还没有开始执行。这意味着当第一个 bus.post 返回时,您将继续下一个帖子,而没有任何介入的订阅者开始处理。
public void post(Object event) Posts an event to all registered
subscribers. This method will return successfully after the event has
been posted to all subscribers, and regardless of any exceptions
thrown by subscribers. If no subscribers have been subscribed for
event's class, and event is not already a DeadEvent, it will be
wrapped in a DeadEvent and reposted.Parameters: event - event to post.