Skip to content

Instantly share code, notes, and snippets.

@DmitrySoshnikov
Last active November 7, 2019 06:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DmitrySoshnikov/4b89bf2e67a72258ab709c97cef4009d to your computer and use it in GitHub Desktop.
Save DmitrySoshnikov/4b89bf2e67a72258ab709c97cef4009d to your computer and use it in GitHub Desktop.
/**
* -------------------------------------------
* Thread-safe vs. Non-thread-safe. Mutex.
* -------------------------------------------
*
* Some resources (like global `cout` stream) have to be guarded
* in order to be thread-safe.
*
* The simplest way to guard is to use a mutex:
*
* mu.lock();
*
* // do the critical section job
*
* mu.unlock();
*
* Alternatively one case use std::lock_guard with RAII,
* which unlocks the mutex automatically, when the guard
* goes out of scope:
*
* lock_guard<mutex> guard(mu);
*
* In the example below we print rows of the array.
* Each thread is assigned to print its row. The first
* three threads operate on a non-thread-safe code,
* accessing randomly `cout` resource. The last three
* threads operate on the guarded code.
*
* by Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
* MIT Style License, 2019
*/
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
static int values[3][10] = {
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100},
{100, 200, 300, 400, 500, 600, 700, 800, 900, 1000},
};
static mutex mu;
void nonThreadSafe(int row) {
for (int i = 0; i < 10; i++) {
cout << values[row][i] << ", ";
}
cout << "\n";
}
void threadSafe(int row) {
// Note: RAII, instead of mu.lock()/mu.unlock()
// when `guard` goes out of scope, `mu` is unlocked.
lock_guard<mutex> guard(mu);
for (int i = 0; i < 10; i++) {
cout << values[row][i] << ", ";
}
cout << "\n";
}
int main(int argc, char const *argv[]) {
cout << "\n----------- Non-thread-safe -------------\n\n";
thread t1(nonThreadSafe, 0);
thread t2(nonThreadSafe, 1);
thread t3(nonThreadSafe, 2);
t1.join(); // main thread waits for t1
t2.detach(); // main thread doesn't wait for t2 (t2 is daemon)
t3.join(); // waits for t3
mu.lock();
cout << "\n----------- Thread-safe -------------\n\n";
mu.unlock();
thread t5(threadSafe, 0);
thread t6(threadSafe, 1);
thread t7(threadSafe, 2);
t5.join();
t6.join();
t7.join();
return 0;
}
/*
Results:
Note: the non-thread-safe results may depend on specific machine
and the thread scheduler. On my execution they are:
----------- Non-thread-safe -------------
1, 2, 3, 104, , 5, 6100, , 200, 300, 20400, 30, 40, 50, 60, , 500, 70, 600807, 90, , 1008, 9, , 10,
700,
, 800, 900, 1000,
----------- Thread-safe -------------
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
100, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
*/
@silentroach
Copy link

save -> safe

@DmitrySoshnikov
Copy link
Author

@silentroach, thanks, my favorite typo :) Fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment