When multiple threads are working on the same data there is the danger that one thread changes the data while another thread tries to read and possibly also change the data at the same time. To avoid such race conditions proper means have to be applied.
Full protection - the safe way
- Mutexes and locks
To allow both changing and reading of shared data by arbitrary threads at any time you can protect data access with use of mutexes and locks. The mutex ensures that only one thread at a time can access the data.
Protect only where really necessary
All synchronization means cause some kind of complexity and will have an impact on execution performance. Sometimes there are simpler possibilities to ensure proper multi threading.
- multiple reader threads - no protection at all If it is possible for your program flow then first fully initialize shared data before reader threads can access them. As long as the data will not change while being accessed from one or more reader threads threre is no need for any synchronization.
- Safe initialization of data - std::call_once, static variables
Within multithreaded environments the trigger for initializing some data may come from different threads at the same time (e.g. request for a singleton instance which is created on such a request).
- Protecting simple integral values - std::atomic
Sometimes your shared data consists only of a single integral value (e.g. some counter, some instance pointer). Instead of having to use a separate mutex you can simply define the data as being "atomic".