Reputation: 29421
Conventional wisdom that async void
is ok in event handlers works great in GUI-driven applications (e.g. WPF).
However, I've recently been bitten by this assumption when working with a different sort of event handlers. Consider an application where an event handler gets called as a result of an external event, such as a RabbitMQ message or some third party integration library. Presumably, the event handler gets called by some kind of dispatcher loop.
If the code in the event handler is synchronous, all is well - it needs to finish executing before the event handler can fire again. But if the event handler is async void
, each call to the event handler is fire-and-forget, and many will execute in parallel.
It is often the case that you don't want this behaviour. For example, if I am processing messages from a RabbitMQ queue, I want to process the messages in order. Yet, I would like to be able to await
asynchronous calls.
How can I have asynchronous event handlers and yet still have them executed sequentially?
Update: I came across a blog post by Stephen Cleary that describes exactly the same problem I'm having. However, if I understand correctly, his suggested use of deferrals assumes that you have control over the event args (which in my case I don't).
Upvotes: 1
Views: 1020
Reputation: 203829
Create a SemaphoreSlim
with a count of 1, and then in the event handler(s) call WaitAsync
before doing any of the work (making sure to Release
it when done). This will ensure that no more than 1 handler is ever doing work at the same time.
If you can control the object firing the event there are better options available to you, but since you don't, you're limited to manually synchronizing all of the handlers with each other.
Upvotes: 1
Reputation: 101483
I think event invokator should not care about such things. There might be multiple subscribers for given event, and all those subscribers might perform asynchronous operation in event handler. You don't want to wait for them all to complete before publishing next event, because suppose subscriber A handles your message in 100ms and subscriber B handles it in 10 seconds - you will then waste subscriber A time for nothing.
Instead, solve that on subscriber side. In event handler put incoming message in some queue and return. Meanwhile process that queue with another thread in background, one by one.
Upvotes: 0