2014-02-22 44 views
11

我使用Guava的EventBus启动一些处理和报告结果。这里是一个非常简单的编译例子: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的相关source code

/** 
    * 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事件,在所有处理程序完成之前调用该事件不会返回。

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

是否有任何方式让公共汽车按照所述的方式运行并产生所需的输出?我没在的Javadoc读取

的EventBus保证它不会同时从多个 线程调用用户的方法,除非法明确允许 它由轴承@AllowConcurrentEvents注解。

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

编辑

这里的问题的原因是,我post从用户中荷兰国际集团。由于事件总线不可重入,这些“子帖子”排队等待并在第一个处理程序完成后处理。我可以在EventBus源文件中注释if (isDispatching.get()) { return; }部分,并且所有内容都按我的预期行事 - 所以真正的问题是我通过这样做引入了哪些潜在问题?看起来设计师做出了一个不让重入的认真决定。

+0

看起来像事件总线运行在它自己的线程。这通常意味着这些操作是异步执行的,并且(尽快它是一个总线)保证按照其顺序执行,并且与主线程无关 – injecteer

+0

@injecteer它不运行它自己的线程。他们有一个'AsyncEventBus',允许你指定一个'Executor' - 但我没有使用它。这全是单线程的。 – zmb

+0

你可能是对的。虽然我认为,他们运行在一个新的线程:)你可以请测试它通过添加'System.out.println(“curr线程:”+ Thread.currentThread()。getName())'到每个处理' @订阅'-d方法? – injecteer

回答

7

EventBus通常操作上的代码发布事件到总线不应该关心什么订户的事​​件或情况下,比其他做的原则,即事件被张贴在被尊重的顺序(无论如何都是同步事件总线的情况)。

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

+0

“将我的代码与响应给定事件时发生的事情分开”*是*我想要的。我希望事件说“准备好要发生的事情” - 我不在乎如何或者什么订户要做好准备。他们可能不需要做任何事情。我只想确信,在发布活动后,用户已准备就绪,以便我可以继续采取一些行动。事件总线在这种情况下看起来很完美,因为我可以轻松地添加和删除可能以不同方式处理事件的组件。发布该事件的代码不在意。 – zmb

+1

到目前为止,我正在回答的是,这种行为是有目的的,事件总线也不会支持我的用例。我最终修改了公共汽车,以允许更适合我的使用案例的重入。 – zmb

2

直到所有“订阅者”都被发信号为止,发布到EventBus才会返回。这些订阅者可能没有开始执行。这意味着,当第一个bus.post返回时,您将继续下一篇文章,而没有任何干预用户开始处理。

public void post(Object event)向所有注册的 订阅者发布事件。在事件已将 发布给所有订阅者以及订阅者抛出任何异常 之后,此方法将成功返回。如果订阅者没有订阅 事件的类,并且事件不是已经是DeadEvent的,那么将包含在DeadEvent中的 并重新发布。

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

+0

这不太对 - 在这种情况下,当第一篇文章返回所有事件已经处理。无论哪种方式 - 这不能回答我的问题,即我是否可以让公交车按照预期行事。 – zmb

5

我试着总结番石榴的EventBus事件传递的行为:

如果事件E1被张贴在此刻T1,所有的用户会收到通知。 如果事件本身,在它的@Subscribe - 方法(一小片刻后),“新”事件E2入列,事后交付订户的职位之一。之后的意思是:毕竟@订阅-方法E1t1确实返回。

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

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