#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

2

There are 2 answers

0
Sam Varshavchik On BEST ANSWER
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");
0
463035818_is_not_an_ai On

When you pass t to the thread constructor a copy is made. And every instance of the class has its own mutex. A mutex that is used by only a single thread is rather useless.

With the free function every call to the function uses the same one mutex.

To pass the object by reference you can use std::ref:

std::thread t1(&ThreadLessons::func,std::ref(t), "Number 1");

Actually still a copy is made, but the copy is of a std::reference_wrapper, which wraps t and acts like a reference. Effectively it is as if t was passed by reference.