2014-02-22 80 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的明確選擇設計。