Async Event Handlers and Consistency

In the context of a C # console application, if I do a loop used to receive a message asynchronously, which triggers an event for every message received, for example:

while (true) { var message = await ReceiveMessageAsync(); ReceivedMessage(new ReceivedMessageEventArgs(message)); } 

Now, if I have several subscribers to the event (say, 3 subscribers for example), they all use an asynchronous event handler, for example:

 async void OnReceivedMessageAsync(object sender, ReceivedMessageEventArgs args) { await TreatMessageAsync(args.Message); } 

Should the message object be encoded in safe stream? I think so, since the TreatMessageAsync code from different event handlers can be executed in a consistent manner for all subscribers (when an event occurs, three async subscribers of event handlers are called, each of which starts an asynchronous operation, which can potentially be executed in a consistent manner on different threads according to the task scheduler). Or am I wrong?

Thanks!

+4
source share
2 answers

You must encode it in a thread-safe manner. The easiest way to do this is to make it immutable.

If you have a true event, its arguments should be unchanged. If you use an event handler for something that is not a true event (like a command or implementation), you can change your API.

It is possible to have concurrent event handlers because each handler will start sequentially, but they can be resumed at the same time.

+2
source

As Stephen said, the easiest way to ensure thread safety in this case is to use indispensable event arguments.

In most cases, even the arguments are used only to notify observers without any requirements for changes from the observed side to the observer (i.e., from the subscriber to the event owner). In some special cases, such as implementing an event chain, the arguments to the template design pattern should be mutable, but in all other cases they should not.

The inevitability in this case will help you not only easily implement compatible handlers, but also lead to a clearer design and improve maintainability, since now you have the opportunity to misuse your API.

Conclusion : you must implement your thread safety methods, but you should be aware that an unhandled exception from the event handler will be swallowed if your event is fired from a thread other than the UI.

There is a danger of using async void methods: if this method fails and you call it in an environment without a synchronization context (for example, from a thread pool thread from a console application), you will get an unhandled domain exception that the application closes:

 internal class Program { static Task Boo() { return Task.Run(() => { throw new Exception("111"); }); } private static async void Foo() { await Boo(); } static void Main(string[] args) { // Application will blow with DomainUnhandled excpeption! try { Foo(); } catch (Exception e) { // Will not catch it here! Console.WriteLine(e); } Console.ReadLine(); } } 
+1
source

All Articles