Remember: there is no way to properly debug a multi-threaded application, as when debugging "while it does not work."
You need to have a way to somehow prove to yourself that there will be no deadlocks and that all data accesses are serialized where they should be. This is quite difficult to do, and even experienced developers may skip some cases.
Thus, it is very important that you develop a multi-threaded application, so that the design guarantees certain properties (for example, freedom from deadlocks). Note that nowhere in the previous sentence is debugging used. It should work on design, not on finding bugs.
There are two main problems:
The event-based or CSP approach, which I will discuss below, automatically takes care of # 1. The deadlock issue is not trivial and remains an active research topic. Namely, ways to prove the absence of a deadlock (as well as development for it!).
One example of a design that simplifies verification and formal confirmation of certain properties, such as freedom from deadlocks, is sequential process transfer (CSP) .
The CSP approach is available in most application development infrastructures and can be implemented as a system driven by nothing more than an event. Main functions:
Streams exchange messages (messages) among themselves.
Data structures are never directly shared between threads, and events themselves have data.
A single synchronization is performed to sequentially access the event queue when sending an event.
This in itself provides freedom from deadlocks until higher-level abstractions use event passing to reuse deadlock abstractions. For example, the problem of interlocking in the philosophy of dining philosophers can be achieved even when an event is transmitted, but now you explicitly transmit information about resources through events (messages). Thus, the problem is at least made explicit, and you are forced to think about it. The problem does not sweep under the mat, so to speak.
Formal methods for proving that no deadlocks can occur are easier to apply to code-generating events that implement CSPs. The event acceptance code can provide an introspection of the run time, which allows you to retrieve a set of received events for each state, as well as a set of states (internally this will be a state machine). This information can often be sufficient to either be unable to reach an impasse, or it can list a small set of deadlock scenarios, which can then be considered otherwise. The CSP formalism usually cannot fully capture the full semantics of software, so semantics themselves can be used to further prove that the deadlock does not occur or that they are otherwise dealt with.