Fluent::Programmer

    Home Blog About
  • Home
  • Blog
  • About

Mutexes - Locking methods in C++ πŸ‘‹

  • Fluent Programmer
  •   6 min read

Quick summary ↬  In C++, a mutex (short for mutual exclusion) is a synchronization primitive used to protect shared resources from simultaneous access by multiple threads. It ensures that only one thread can access the protected resource at a given time, preventing data races and maintaining thread safety.

Introduction

Mutexes provide two fundamental operations:

Locking: A thread can lock a mutex to gain exclusive access to the protected resource. If the mutex is already locked by another thread, the thread attempting to lock it will be blocked until the mutex becomes available.

Unlocking: Once a thread has finished accessing the protected resource, it must unlock the mutex to release its ownership. This allows other threads to acquire the mutex and access the resource.

Consider the below code,

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>
#include <thread>
#include <mutex>
#include <shared_mutex>

// mutex, 
// recursive_mutex, 
// shared_mutex, 
// timed_mutex, 
// shared_timed_mutex, 
// recursive_timed_mutex


// Example using std::mutex
std::mutex mutex1;

void mutexExample()
{
    std::lock_guard<std::mutex> lock(mutex1);
    std::cout << "Thread " << std::this_thread::get_id() << " acquired std::mutex." << std::endl;
    // Critical section
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

// Example using std::recursive_mutex
std::recursive_mutex recursiveMutex;

void recursiveMutexExample(int depth)
{
    std::lock_guard<std::recursive_mutex> lock(recursiveMutex);
    std::cout << "Thread " << std::this_thread::get_id() << " acquired std::recursive_mutex (depth: " << depth << ")." << std::endl;
    // Recursive call
    if (depth > 0)
        recursiveMutexExample(depth - 1);
}

// Example using std::shared_mutex
std::shared_mutex sharedMutex;
int sharedData = 0;

void sharedMutexExample(bool writeAccess)
{
    if (writeAccess)
    {
        std::unique_lock<std::shared_mutex> lock(sharedMutex);
        sharedData++;
        std::cout << "Thread " << std::this_thread::get_id() << " acquired std::shared_mutex (write access). Data: " << sharedData << std::endl;
    }
    else
    {
        std::shared_lock<std::shared_mutex> lock(sharedMutex);
        std::cout << "Thread " << std::this_thread::get_id() << " acquired std::shared_mutex (read access). Data: " << sharedData << std::endl;
    }
}

int main()
{
    // Example with std::mutex
    std::thread t1(mutexExample);
    std::thread t2(mutexExample);
    t1.join();
    t2.join();

    // Example with std::recursive_mutex
    std::thread t3([&]() { recursiveMutexExample(3); });
    t3.join();

    // Example with std::shared_mutex
    std::thread t4([&]() { sharedMutexExample(false); });
    std::thread t5([&]() { sharedMutexExample(true); });
    std::thread t6([&]() { sharedMutexExample(false); });
    t4.join();
    t5.join();
    t6.join();

    return 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
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <chrono>

// Example using std::shared_timed_mutex
std::shared_timed_mutex sharedTimedMutex;
int sharedData = 0;

void sharedTimedMutexExample(bool writeAccess)
{
    if (writeAccess)
    {
        if (sharedTimedMutex.try_lock_for(std::chrono::seconds(2)))
        {
            sharedData++;
            std::cout << "Thread " << std::this_thread::get_id() << " acquired std::shared_timed_mutex (write access). Data: " << sharedData << std::endl;
            sharedTimedMutex.unlock();
        }
        else
        {
            std::cout << "Thread " << std::this_thread::get_id() << " failed to acquire std::shared_timed_mutex (write access)." << std::endl;
        }
    }
    else
    {
        if (sharedTimedMutex.try_lock_shared_for(std::chrono::seconds(2)))
        {
            std::cout << "Thread " << std::this_thread::get_id() << " acquired std::shared_timed_mutex (read access). Data: " << sharedData << std::endl;
            sharedTimedMutex.unlock_shared();
        }
        else
        {
            std::cout << "Thread " << std::this_thread::get_id() << " failed to acquire std::shared_timed_mutex (read access)." << std::endl;
        }
    }
}

int main()
{
    // Example with std::shared_timed_mutex
    std::thread t1([&]() { sharedTimedMutexExample(false); });
    std::thread t2([&]() { sharedTimedMutexExample(true); });
    std::thread t3([&]() { sharedTimedMutexExample(false); });
    t1.join();
    t2.join();
    t3.join();

    return 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

// Example using std::timed_mutex
std::timed_mutex timedMutex;

void timedMutexExample()
{
    if (timedMutex.try_lock_for(std::chrono::seconds(2)))
    {
        std::cout << "Thread " << std::this_thread::get_id() << " acquired std::timed_mutex." << std::endl;
        // Critical section
        std::this_thread::sleep_for(std::chrono::seconds(3));
        timedMutex.unlock();
    }
    else
    {
        std::cout << "Thread " << std::this_thread::get_id() << " failed to acquire std::timed_mutex." << std::endl;
    }
}

// Example using std::recursive_timed_mutex
std::recursive_timed_mutex recursiveTimedMutex;

void recursiveTimedMutexExample(int depth)
{
    if (recursiveTimedMutex.try_lock_for(std::chrono::seconds(1)))
    {
        std::cout << "Thread " << std::this_thread::get_id() << " acquired std::recursive_timed_mutex (depth: " << depth << ")." << std::endl;
        // Recursive call
        if (depth > 0)
            recursiveTimedMutexExample(depth - 1);
        recursiveTimedMutex.unlock();
    }
    else
    {
        std::cout << "Thread " << std::this_thread::get_id() << " failed to acquire std::recursive_timed_mutex." << std::endl;
    }
}

int main()
{
    // Example with std::timed_mutex
    std::thread t1(timedMutexExample);
    std::thread t2(timedMutexExample);
    t1.join();
    t2.join();

    // Example with std::recursive_timed_mutex
    std::thread t3([&]() { recursiveTimedMutexExample(3); });
    t3.join();

    return 0;
}

Using a mutex is essential in multi-threaded programs to protect shared resources and avoid race conditions that can lead to unpredictable behavior or data corruption. By properly synchronizing access to shared data with mutexes, you can ensure the integrity and consistency of your program’s execution.

About The Author

Fluentprogrammer doesn't need coffee to program. They run on pure caffeine and lines of code.

Email Newsletter

Table of Contents

  • Introduction
  • C++
  • Beautiful code series

Unhealthy love with dark corners of C++

Founded by an engineer to help engineers. 2021–2023.

  • About us
  • Privacy policy