This GotW was written to answer a set of related frequently asked questions. So here’s a mini-FAQ on “thread safety and synchronization in a nutshell,” and the points we’ll cover apply to thread safety and synchronization in pretty much any mainstream language.
1. What is a race condition, and how serious is it?
2. What is a correctly synchronized program? How do you achieve it? Be specific.
3. Consider the following code, where some_obj is a shared variable visible to multiple threads.
// thread 1 (performs no additional synchronization)
code_that_reads_from( some_obj ); // passes some_obj by const &
// thread 2 (performs no additional synchronization)
code_that_modifies( some_obj ); // passes some_obj by non-const &
If threads 1 and 2 can run concurrently, is this code correctly synchronized if the type of some_obj is:
Hint: This is actually a two-part question, not a seven-part question. There are only two unique answers, each of which covers a subset of the cases.
4. External synchronization means that the code that uses/owns a given shared object is responsible for performing synchronization on that object. Answer the following questions related to external synchronization:
(a) What is the normal external synchronization responsibility of code that owns and uses a given shared variable?
(b) What is the “basic thread safety guarantee” that all types must obey to enable calling code to perform normal external synchronization?
(c) What partial internal synchronization can still be required within the shared variable’s implementation?
5. Full internal synchronization (a.k.a. “synchronized types” or “thread-safe types”) means that a shared object performs all necessary synchronization internally within that object, so that calling code does not need to perform any external synchronization. What types should be fully internally synchronized, and why?