When a cross-thread message is expected to cause a visible side effect, it’s best to use an asynchronous approach—namely, PostMessage(). This ensures that the message is queued and handled in its natural order, rather than being injected immediately into the middle of another message’s processing due to Windows’ fiber-based reentrancy.
In practice, synchronous SendMessage() can lead to unplanned side effects by interrupting ongoing operations. If a side effect (like updating shared state) is expected, using PostMessage() prevents these side effects from "happening on the fly" in an inconsistent context. Even with PostMessage(), if the side effects involve shared resources, additional synchronization (events, semaphores, MMIO, etc.) may be needed to ensure state consistency across threads.
It is important to maintain a consistent execution context when multiple threads interact, so as to avoid the pitfalls of unintended reentrancy and unsynchronized shared state modifications. If a cross-thread SendMessage() tries to modify a shared state, it's likely a bug, and a mutex won't help.