In MP2, we have a multi-threaded messaging approach. The message broker service in Core maintains several named message "channels" where messages can be passed. Message broadcasters use the IMessageBroker interface to send messages through the system.
Each message consumer creates an own message queue instance. Typically, message consumers receive messages asynchronously, by using the AsynchronousMessageQueue class. But there is also a synchronous version, the SynchronousMessageQueue. Each message queue instance maintains its own input queue of messages. A message queue can be registered at all message channels which are interesting for the consumer. The message input queues in all AsynchronousMessageQueue and SynchronousMessageQueue instances are filled synchronously by the message broker when a new message is sent by a message broadcaster.
Synchronous message queues will then synchronously call their consumer's message handler method(s), but there are several restrictions for receiving messages synchronously. Consumers must not aquire locks while they block the synchronous message thread. They also must not block the calling thread for doing long-term calculations, i.e. the thread must be released quickly. Those restrictions are documented in the class docs of SynchronousMessageQueue.
Each asynchronous message queue has its own message delivery thread which dequeues incoming messages from the input message queue as soon as possible. The message is then delivered to the client message handler method(s). In contrast to the synchronous message handlers, those asynchronous message handlers may block the message delivery thread (it is their "own" thread because they have instantiated the asynchronous message queue by their own and each AMQ provides its own message delivery thread; that thread doesn't need to execute other work in the system).
Messaging and the MP2 multithreading guideline
The MP2 multithreading guideline has many implications on the sending and receiving of messages. The messaging system together with the usage patterns shown at the Message Broker page were created to fulfil all of those guidelines.
Why not .net events?
To be continued: Describe that we want to avoid module-overlapping references. Furthermore, .net events create strong references; the MessageBroker doesn't.