Internally Synchronized Class

  • Multiple threads accessing the same memory location

    • With modification
    • Must be synchronized to prevent a data race
  • C++ STL containers need to be externally synchronized

    • e.g. by locking a mutex before calling a member function
  • Our own types can provide internal synchronization

    • An std::mutex as a data member
    • The member functions lock the mutex before accessing the class's data
    • They unlock the mutex after accessing the class's data

Wrapper for std::vector

  • std::vector acts as a memory location

    • We may need to lock a mutex before calling its member function
  • Alternatively, we could write an internally synchronized wrapper for it

  • A class which

    • Has an std::vector data member
    • Has an std::mutex data member
    • Member functions which lock the mutex before accessing the std::vector
    • Then unlock the mutex after accessing it
  • An internally synchronized class


#include <functional>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

using namespace std::literals;

// Very simplistic thread-safe vector class
class Vector {
  std::mutex mut;       // Mutex as private class data member
  std::vector<int> vec; // Shared data - mutex protects access to it
public:
  void push_back(const int &i) {
    mut.lock();       // Lock the mutex
    vec.push_back(i); // Critical section
    mut.unlock();     // Unlock the mutex
  }

  void print() {
    mut.lock();

    for (auto i : vec) {
      std::cout << i << ", ";
    }

    mut.unlock();
  }
};

void func(Vector &vec) {
  for (int i = 0; i < 5; ++i) {
    vec.push_back(i);
    std::this_thread::sleep_for(50ms);
    vec.print();
  }
}

int main() {
  Vector vec;

  std::thread thr1(func, std::ref(vec));
  std::thread thr2(func, std::ref(vec));
  std::thread thr3(func, std::ref(vec));

  thr1.join();
  thr2.join();
  thr3.join();
}