Я использую 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
от абонента. Поскольку шина событий не является реентерабельной, эти "подзаголовки" попадают в очередь и обрабатываются после завершения первого обработчика. Я могу прокомментировать if (isDispatching.get()) { return; }
if (isDispatching.get()) { return; }
в источнике EventBus
и все ведет себя так, как я ожидал, - так что реальный вопрос заключается в том, какие потенциальные проблемы я представил, сделав это? Похоже, что дизайнеры приняли добросовестное решение не допускать повторного участия.