【发布时间】:2014-02-22 00:32:16
【问题描述】:
我正在使用 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 事件,在所有处理程序完成之前调用事件不会返回。
我不太明白这种实现的原因。当然,可以保证事件是有序的,但是周围代码的顺序会完全扭曲。
有什么方法可以让总线按照描述的方式运行并产生所需的输出?我确实在 Javadocs 中读到了
EventBus 保证它不会从 多个线程同时进行,除非该方法明确允许 带有@AllowConcurrentEvents 注释。
但我认为这不适用于这里 - 我在单线程应用程序中看到了这个问题。
编辑
这里问题的原因是我来自订阅者的posting。由于事件总线不可重入,因此这些“子帖子”会排队并在第一个处理程序完成后处理。我可以注释掉EventBus 源代码中的if (isDispatching.get()) { return; } 部分,一切都按照我的预期运行——所以真正的问题是我这样做引入了哪些潜在问题?设计师们似乎做出了一个谨慎的决定,不允许重入。
【问题讨论】:
-
看起来事件总线在它自己的线程中运行。这通常意味着,这些操作是异步执行的,并且(只要它是总线)保证按照其顺序交付,并且与主线程无关
-
@injecteer 它不运行它自己的线程。他们确实有一个
AsyncEventBus允许您指定一个Executor- 但我没有使用它。这都是单线程的。 -
你可能是对的。虽然我认为,它们确实在一个新线程中运行 :) 您能否通过在每个处理
@Subscribe-d 方法中添加System.out.println( "curr thread: " + Thread.currentThread().getName() )来测试它? -
你为什么要从 within 订阅者的 .post() 开始?这里有问题
-
这正是我看到这个问题的原因。这不是一个有效的用例吗?从概念上讲,事件可以触发其他事件 - 我们一直在假设这没问题的情况下进行操作。