With multithreading, you have to be careful to not create race conditions. The default way to do this is the usage of mutexes at all critical sections. This is annoying, reduces performance and can lead to deadlocks. For some scenarios, it’s a possibility to instead use atomics or lock-free containers.
The default way
Mutexes
… can be used to sync anything.
std::mutex mtx;
int num = 0;
void inc() {
  std::lock_guard<std::mutex> lock(mtx);
  num++;
}
int main() {
  std::thread t1(inc), t2(inc);
  return 0;
}
Alternatives
Atomics
… are used to sync primitive types.
std::atomic<int> num = 0;
void inc() {
  num++;
}
int main() {
  std::thread t1(inc), t2(inc);
  return 0;
}
Depending on the use case, atomics are not necessarily more performant than locks.
Lock-free containers
… don’t need to sync at all.
A very famous example is oneTBB from Intel:
- tbb::concurrent_queue
- tbb::concurrent_unordered_map
- tbb::concurrent_vector
You can just use supported containers, and not all operations necessarily are thread-safe.
