Skip to content

4. Locks

Recursive mutex

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Two shoppers adding garlic and potatoes to a shared notepad
#include <mutex>
#include <thread>

unsigned int garlic_count = 0;
unsigned int potato_count = 0;
std::mutex pencil;

void add_garlic() {
  pencil.lock();
  ++garlic_count;
  pencil.unlock();
}

void add_potato() {
  pencil.lock();
  ++potato_count;
  pencil.unlock();
}

void shopper() {
  for (int i = 0; i < 10000; ++i) {
    add_garlic();
    add_potato();
  }
}

int main() {
  std::thread barron(shopper);
  std::thread olivia(shopper);
  barron.join();
  olivia.join();
  printf("We should buy %u garlic.\n", garlic_count);
  printf("We should buy %u potatoes.\n", potato_count);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Two shoppers adding garlic and potatoes to a shared notepad

#include <mutex>
#include <thread>

unsigned int garlic_count = 0;
unsigned int potato_count = 0;
std::recursive_mutex pencil;

void add_garlic() {
  pencil.lock();
  ++garlic_count;
  pencil.unlock();
}

void add_potato() {
  pencil.lock();
  ++potato_count;
  add_garlic();
  pencil.unlock();
}

void shopper() {
  for (int i = 0; i < 10000; ++i) {
    add_garlic();
    add_potato();
  }
}

int main() {
  std::thread barron(shopper);
  std::thread olivia(shopper);
  barron.join();
  olivia.join();
  printf("We should buy %u garlic.\n", garlic_count);
  printf("We should buy %u potatoes.\n", potato_count);
}

Try lock

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Two shoppers adding items to a shared notepad
#include <chrono>
#include <mutex>
#include <thread>

unsigned int items_on_notepad = 0;
std::mutex pencil;

void shopper(const char* name) {
  int items_to_add = 0;
  while (items_on_notepad <= 20) {
    if (items_to_add) {  // add item(s) to shared items_on_notepad
      pencil.lock();
      items_on_notepad += items_to_add;
      printf("%s added %u item(s) to notepad.\n", name, items_to_add);
      items_to_add = 0;
      std::this_thread::sleep_for(
          std::chrono::milliseconds(300));  // time spent writing
      pencil.unlock();
    } else {  // look for other things to buy
      std::this_thread::sleep_for(
          std::chrono::milliseconds(100));  // time spent searching
      ++items_to_add;
      printf("%s found something else to buy.\n", name);
    }
  }
}

int main() {
  auto start_time = std::chrono::steady_clock::now();
  std::thread barron(shopper, "Barron");
  std::thread olivia(shopper, "Olivia");
  barron.join();
  olivia.join();
  auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(
                          std::chrono::steady_clock::now() - start_time)
                          .count();
  printf("Elapsed Time: %.2f seconds\n", elapsed_time / 1000.0);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Two shoppers adding items to a shared notepad
#include <chrono>
#include <mutex>
#include <thread>

unsigned int items_on_notepad = 0;
std::mutex pencil;

void shopper(const char* name) {
  int items_to_add = 0;
  while (items_on_notepad <= 20) {
    if (items_to_add &&
        pencil.try_lock()) {  // add item(s) to shared items_on_notepad
      items_on_notepad += items_to_add;
      printf("%s added %u item(s) to notepad.\n", name, items_to_add);
      items_to_add = 0;
      std::this_thread::sleep_for(
          std::chrono::milliseconds(300));  // time spent writing
      pencil.unlock();
    } else {  // look for other things to buy
      std::this_thread::sleep_for(
          std::chrono::milliseconds(100));  // time spent searching
      ++items_to_add;
      printf("%s found something else to buy.\n", name);
    }
  }
}

int main() {
  auto start_time = std::chrono::steady_clock::now();
  std::thread barron(shopper, "Barron");
  std::thread olivia(shopper, "Olivia");
  barron.join();
  olivia.join();
  auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(
                          std::chrono::steady_clock::now() - start_time)
                          .count();
  printf("Elapsed Time: %.2f seconds\n", elapsed_time / 1000.0);
}

Shared mutext

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Several users reading a calendar, but only a few users updating it
#include <array>
#include <chrono>
#include <mutex>
#include <thread>

char WEEKDAYS[7][10] = {"Sunday",   "Monday", "Tuesday", "Wednesday",
                        "Thursday", "Friday", "Saturday"};
int today = 0;
std::mutex marker;

void calendar_reader(const int id) {
  for (int i = 0; i < 7; ++i) {
    marker.lock();
    printf("Reader-%d sees today is %s\n", id, WEEKDAYS[today]);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    marker.unlock();
  }
}

void calendar_writer(const int id) {
  for (int i = 0; i < 7; ++i) {
    marker.lock();
    today = (today + 1) % 7;
    printf("Writer-%d updated date to %s\n", id, WEEKDAYS[today]);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    marker.unlock();
  }
}

int main() {
  // create ten reader threads ...but only two writer threads
  std::array<std::thread, 10> readers;
  for (unsigned int i = 0; i < readers.size(); ++i) {
    readers[i] = std::thread(calendar_reader, i);
  }
  std::array<std::thread, 2> writers;
  for (unsigned int i = 0; i < writers.size(); ++i) {
    writers[i] = std::thread(calendar_writer, i);
  }

  // wait for readers and writers to finish
  for (unsigned int i = 0; i < readers.size(); ++i) {
    readers[i].join();
  }
  for (unsigned int i = 0; i < writers.size(); ++i) {
    writers[i].join();
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Several users reading a calendar, but only a few users updating it
#include <array>
#include <chrono>
#include <mutex>
#include <shared_mutex>
#include <thread>

char WEEKDAYS[7][10] = {"Sunday",   "Monday", "Tuesday", "Wednesday",
                        "Thursday", "Friday", "Saturday"};
int today = 0;
std::shared_mutex marker;

void calendar_reader(const int id) {
  for (int i = 0; i < 7; ++i) {
    marker.lock_shared();
    printf("Reader-%d sees today is %s\n", id, WEEKDAYS[today]);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    marker.unlock_shared();
  }
}

void calendar_writer(const int id) {
  for (int i = 0; i < 7; ++i) {
    marker.lock();
    today = (today + 1) % 7;
    printf("Writer-%d updated date to %s\n", id, WEEKDAYS[today]);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    marker.unlock();
  }
}

int main() {
  // create ten reader threads ...but only two writer threads
  std::array<std::thread, 10> readers;
  for (unsigned int i = 0; i < readers.size(); ++i) {
    readers[i] = std::thread(calendar_reader, i);
  }
  std::array<std::thread, 2> writers;
  for (unsigned int i = 0; i < writers.size(); ++i) {
    writers[i] = std::thread(calendar_writer, i);
  }

  // wait for readers and writers to finish
  for (unsigned int i = 0; i < readers.size(); ++i) {
    readers[i].join();
  }
  for (unsigned int i = 0; i < writers.size(); ++i) {
    writers[i].join();
  }
}