Search code examples
c++multithreadingmutexshared-resourcelock-guard

mutex used in a function defined inside a class doesn't seem to work when that function is called in a thread in main


#include <thread>
#include <iostream>
#include <mutex>

class ThreadLessons {
private:
    std::mutex _threading_mutex_in_class;
public:
    ThreadLessons() {}
    ThreadLessons(const ThreadLessons &tl) {}
    ThreadLessons operator=(const ThreadLessons &tl) {return *this;}
    
    void func(std::string s) {
        std::unique_lock lg(_threading_mutex_in_class);
        std::cout << std::endl;
        for(int i{0}; i<10; i++) {
            std::cout << s << std::endl;
        }
    }
};

std::mutex _threading_mutex;

void func(std::string s) {
    std::unique_lock lg(_threading_mutex);
    std::cout << std::endl;
    for(int i{0}; i<10; i++) {
        std::cout << s << std::endl;
    }
}

int main()
{
    std::cout << "Starting threading of function from another class in same file" << std::endl;
    ThreadLessons t;
    std::thread t1(&ThreadLessons::func, t, "Number 1");
    std::thread t2(&ThreadLessons::func, t, "Number 2");
    std::thread t3(&ThreadLessons::func, t, "Number 3");

    t1.join();
    t2.join();
    t3.join();
    
    std::cout << "Starting threading of function from main" << std::endl;
    std::thread t4(func, "Number 4");
    std::thread t5(func, "Number 5");
    std::thread t6(func, "Number 6");

    t4.join();
    t5.join();
    t6.join();
    
    return 0;
}

Output:

Starting threading of function from another class in same file

Number 1
Number 1
Number 1
Number 2
Number 2
Number 2


Number 3
Number 3
Number 1Number 2
Number 2
Number 3
Number 3Number 1


Number 2
Number 1Number 3Number 2

Number 2
Number 3
Number 3
Number 1
Number 1
Number 2
Number 1Number 3


Number 2
Number 1
Number 3
Number 3
Starting threading of function from main

Number 4
Number 4
Number 4
Number 4
Number 4
Number 4
Number 4
Number 4
Number 4
Number 4

Number 5
Number 5
Number 5
Number 5
Number 5
Number 5
Number 5
Number 5
Number 5
Number 5

Number 6
Number 6
Number 6
Number 6
Number 6
Number 6
Number 6
Number 6
Number 6
Number 6

When I call the func function from ThreadLessons class, as seen in output, Number 1, Number 2 and Number 3 output to std::cout is not deterministic like it should be when using a mutex on which a lock was acquired. Notice how Number 4, Number 5 and Number 6 is deterministic and in sequence

Why would it not work for mutex defined in a function inside a class?

Tried using std::cout as a shared resource and expected that it would be used as one when being used from inside a function of a class that locks the mutex


Solution

  • std::thread t1(&ThreadLessons::func, t, "Number 1");
    std::thread t2(&ThreadLessons::func, t, "Number 2");
    std::thread t3(&ThreadLessons::func, t, "Number 3");
    

    In C++ when you pass something to a function, the object is passed by value. This effectively means that a copy of the object is made.

    Here, what ends up happening is three copies of the original object is made. Each call to std::thread's constructor copies t, and each thread has its own object and its own mutex.

    You had to override the copy constructor before this could compile, since std::mutex is not copyable. That should've been a big, red, honking clue: copies are being made here.

    Instead, std::thread has a useful overload that takes a pointer to the object, instead:

    std::thread t1(&ThreadLessons::func, &t, "Number 1");
    std::thread t2(&ThreadLessons::func, &t, "Number 2");
    std::thread t3(&ThreadLessons::func, &t, "Number 3");