Java Guava EventBus 调度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21947936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 11:34:25  来源:igfitidea点击:

Guava EventBus dispatching

javaguava

提问by zmb

I'm using Guava's EventBus to kick off some processing and report results. Here's a very simple compilable example:

我正在使用 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());
    }
}

I use these events as a way for other software components to react in preparation for this processing. For example, they may have to save their current state before processing and restore it after.

我使用这些事件作为其他软件组件做出反应以准备此处理的一种方式。例如,他们可能必须在处理前保存当前状态并在处理后恢复。

I would expect the output of this program to be:

我希望这个程序的输出是:

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

Instead, the actual output is:

相反,实际输出是:

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

The event that is supposed to indicate that processing has started actually happens after the actual processing ("generating results").

应该指示处理已开始的事件实际上发生在实际处理之后(“生成结果”)。

After looking at the source code, I understand why it's behaving this way. Here's the relevant source codefor the EventBus.

查看源代码后,我明白为什么它会这样。下面是相关的源代码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)

What's happening is since I'm already dispatching the top level InitiateProcessingevent, the rest of the events just get pushed to the end of the queue. I would like this to behave similar to .NET events, where invoking the event doesn't return until all handlers have completed.

发生的事情是因为我已经在调度顶级InitiateProcessing事件,其余的事件只是被推送到队列的末尾。我希望它的行为类似于 .NET 事件,其中在所有处理程序完成之前调用事件不会返回。

I don't quite understand the reason for this implementation. Sure, the events are guaranteed to be in order, but the order of the surrounding code gets completely distorted.

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

Is there any way to get the bus to behave as described and produce the desired output? I did read in the Javadocs that

有没有办法让总线按照描述的方式运行并产生所需的输出?我确实在 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.

EventBus 保证它不会同时从多个线程调用订阅者方法,除非该方法通过带有 @AllowConcurrentEvents 注释显式允许它。

But I don't think this applies here - I'm seeing this issue in a single threaded application.

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

Edit

编辑

The cause of the issue here is that I'm posting from within a subscriber. Since the event bus is not reentrant, these "sub-posts" get queued up and are handled after the first handler completes. I can comment out the if (isDispatching.get()) { return; }section in the EventBussource and everything behaves as I would expect - so the real question is what potential problems have I introduced by doing so? It seems the designers made a conscientious decision to not allow reentrancy.

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

采纳答案by ColinD

EventBusgenerally operates on the principle that the code posting an event to the bus shouldn't care about what the subscribers do with the events or when, other than that the order the events were posted in is respected (in the case of a synchronous event bus anyway).

EventBus通常的操作原则是,将事件发布到总线的代码不应该关心订阅者如何处理事件或何时,除了尊重发布事件的顺序(在同步事件总线的情况下)反正)。

If you want specific methods to be called at specific times in the course of your method and you want to be sure those methods complete before your method continues (as you seem to in your example), why not call those methods directly? When you use an event bus, you're explicitly separating your code from what exactly happens in response to a given event. This is desirable in many cases and is the main reason EventBusexists, but it doesn't seem to be quite what you want here.

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

回答by ErstwhileIII

While posting to the EventBus does not return until all "subscribers" have been signaled .. those subscribers might NOT have begun execution. That means that when the first bus.post returns, you continue the next post without any intervening subscriber having begun to process.

虽然在所有“订阅者”都收到信号之前不会返回到 EventBus ......这些订阅者可能还没有开始执行。这意味着当第一个 bus.post 返回时,您将继续下一个 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.

public void post(Object event) 向所有注册订阅者发布一个事件。在将事件发布给所有订阅者后,无论订阅者抛出任何异常,此方法都会成功返回。如果没有订阅者订阅事件的类,并且事件还不是 DeadEvent,它将被包装在 DeadEvent 中并重新发布。

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

回答by Peti

I try to summarize Guava's EventBus event delivery behaviour:

我尝试总结 Guava 的 EventBus 事件传递行为:

If an event E1is posted at moment t1, all subscribers are notified. If one of the subscribers posts an event itself in it's @Subscribe-method (a tiny moment later), the "new" event E2is enqueued and delivered afterwards. Afterwards means here: after all @Subscribe-methods for E1from t1did return.

如果事件E1在时刻t1发布,则通知所有订阅者。如果其中一个订阅者在它的@Subscribe-method 中发布事件本身(片刻之后),“新”事件E2 将被加入队列并随后传递。之后的意思是:毕竟来自t1 的E1 的@Subscribe-methods确实返回了。

Compare this kind of "cascaded" event posting to breadth first tree traversal.

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

It seems to be the explicit choosen design of EventBus.

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

回答by mwoodman

I know this question is 4 years old, but I just ran into the same problem today. There is a simple (and counter-intuitive) change to get the behavior you want. Per https://stackoverflow.com/a/53136251/1296767, you can use an AsyncEventBus with a DirectExecutor:

我知道这个问题已经有 4 年历史了,但我今天遇到了同样的问题。有一个简单的(和违反直觉的)改变来获得你想要的行为。根据https://stackoverflow.com/a/53136251/1296767,您可以将 AsyncEventBus 与 DirectExecutor 一起使用:

public static EventBus bus = new AsyncEventBus(MoreExecutors.newDirectExecutorService());

Running your test code with the above change, the results are exactly what you want:

使用上述更改运行您的测试代码,结果正是您想要的:

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