[英]Guava EventBus dispatching
我正在使用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批注明確允許它。
但我不認為這適用於此 - 我在單線程應用程序中看到了這個問題。
編輯
這里的問題的原因是我post
ING從用戶中。 由於事件總線不可重入,因此這些“子帖子”排隊並在第一個處理程序完成后處理。 我可以注釋掉if (isDispatching.get()) { return; }
if (isDispatching.get()) { return; }
在部分EventBus
源和一切行為與我所期望的-所以真正的問題是什么潛在的問題,我已介紹了這樣做? 似乎設計師做出了一個盡責的決定,不允許重入。
EventBus
通常基於以下原則運行:將事件發布到總線的代碼不應關心訂戶對事件的處理方式,也不關心事件被發布的順序(在同步事件的情況下)巴士無論如何)。
如果您希望在方法過程中的特定時間調用特定方法,並且希望確保在方法繼續之前完成這些方法(如您在示例中所示),為什么不直接調用這些方法? 當您使用事件總線時,您明確地將代碼與響應給定事件的確切事件分開。 這在許多情況下是可取的,並且是EventBus
存在的主要原因,但它似乎並不是你想要的。
我試着總結一下Guava的EventBus事件傳遞行為:
如果在時刻t1發布事件E1 ,則通知所有訂戶。 如果其中一個訂閱者在其@Subscribe -method中發布了一個事件本身(稍后一刻),那么“新”事件E2將被排隊並隨后發送。 之后意味着:畢竟@Subscribe -tthods for E1 from t1確實返回了。
將這種“級聯”事件發布與廣度優先樹遍歷進行比較。
它似乎是EventBus的明確選擇設計。
雖然發布到EventBus不會返回,直到所有“訂閱者”都已發出信號。這些訂閱者可能尚未開始執行。 這意味着當第一個bus.post返回時,您繼續下一個帖子,而沒有任何干預用戶開始處理。
public void post(Object event)將事件發布給所有注冊用戶。 事件發布到所有訂閱者后,此方法將成功返回,並且無論訂閱者拋出任何異常。 如果沒有訂閱者訂閱了事件的類,並且事件不是DeadEvent,它將被包裝在DeadEvent中並重新發布。
參數:event - 要發布的事件。
我知道這個問題已經有4年了,但我今天遇到了同樣的問題。 有一個簡單(和反直覺)的變化來獲得你想要的行為。 根據https://stackoverflow.com/a/53136251/1296767 ,您可以將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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.