Despacho do Guava EventBus
Estou usando o EventBus da Guava para iniciar alguns processamentos e relatar resultados. Aqui está um exemplo compilável muito simples:
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());
}
}
Eu uso esses eventos como uma maneira de outros componentes de software reagirem em preparação para esse processamento. Por exemplo, eles podem ter que salvar seu estado atual antes do processamento e restaurá-lo depois.
Eu esperaria que a saída deste programa seja:
Got processing request - starting processing
Processing has started
Generating results
got results
Generating more results
got results
Processing has completed
Em vez disso, a saída real é:
Got processing request - starting processing
Generating results
Generating more results
Processing has started
got results
got results
Processing has completed
O evento que deve indicar que o processamento foi iniciado realmente ocorre após o processamento real ("geração de resultados").
Depois de analisar o código fonte, entendo por que ele está se comportando dessa maneira. Aqui está o relevanteCódigo fonte para oEventBus
.
/**
* 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)
O que está acontecendo é que já estou despachando o nível superiorInitiateProcessing
evento, o restante dos eventos é empurrado para o final da fila. Gostaria que isso se comportasse de maneira semelhante aos eventos do .NET, onde a invocação do evento não retornará até que todos os manipuladores tenham sido concluídos.
Não entendo bem o motivo dessa implementação. Certamente, os eventos são garantidos em ordem, mas a ordem do código ao redor fica completamente distorcida.
Existe alguma maneira de fazer o barramento se comportar conforme descrito e produzir a saída desejada? Eu li nos Javadocs que
O EventBus garante que ele não chamará um método de assinante de vários threads simultaneamente, a menos que o método permita explicitamente carregando a anotação @AllowConcurrentEvents.
Mas acho que isso não se aplica aqui - estou vendo esse problema em um único aplicativo encadeado.
Editar
A causa do problema aqui é que eu soupost
de dentro de um assinante. Como o barramento de eventos não é reentrante, essas "sub-postagens" são colocadas na fila e são tratadas após a conclusão do primeiro manipulador. Posso comentar oif (isDispatching.get()) { return; }
seção noEventBus
fonte e tudo se comporta como eu esperaria - então a verdadeira questão é quais problemas potenciais eu apresentei ao fazer isso? Parece que os designers tomaram uma decisão consciente de não permitir a reentrada.