0

when I try to use mutex with RAII.

class MutexLock
{
public:
    MutexLock() {
        pthread_mutex_init(&mutex_, NULL);
        cout << "construct of MutexLock" << endl;
    }
    ~MutexLock() {
        cout << "deconstruct of MutexLock" << endl;
        pthread_mutex_destroy(&mutex_);
    }

    void lock() {
        pthread_mutex_lock(&mutex_);
    }

    void unlock() {
        pthread_mutex_unlock(&mutex_);
    }

private:
    MutexLock(const MutexLock &);
    MutexLock& operator=(const MutexLock &); 

    pthread_mutex_t mutex_;
};

class MutexLockGuard
{
public:
    explicit MutexLockGuard(MutexLock &mutex): mutex_(mutex) {
        cout << "construct of MutexLockGuard" << endl;
        mutex_.lock();
    }
    ~MutexLockGuard() {
        cout << "deconstruct of MutexLockGuard" << endl;
        mutex_.unlock();
    }

private:
    MutexLockGuard(const MutexLock &);
    MutexLockGuard& operator=(const MutexLock &);
    MutexLock &mutex_;
};


MutexLock mutex;
int cnt = 5;

void *f(void *arg){
    long t_num = (long) arg;
    while(true){
        MutexLockGuard lock(mutex);
        if(cnt>0){
            usleep(1);
            cout << "args: " << t_num << " " << "cnt: " << cnt-- << endl; 
        } 
        else{break;}       
    }
    return NULL;
}

int main()
{
    pthread_t tid, tid1, tid2, tid3;
    int ret = pthread_create(&tid, NULL, f,(void*)11);
    if(ret == -1){
        perror("create error\n");
    }

    ret = pthread_create(&tid1, NULL, f, (void*)22);
    if(ret == -1){
        perror("create error\n");
    }

    ret = pthread_create(&tid2, NULL, f, (void*)33);
    if(ret == -1){
        perror("create error\n");
    }

    ret = pthread_create(&tid3, NULL, f, (void*)44);
    if(ret == -1){
        perror("create error\n");
    }

    pthread_join(tid, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    return 0;
}

The result shows as

construct of MutexLock
construct of MutexLockGuard
construct of MutexLockGuard
construct of MutexLockGuard
construct of MutexLockGuard
args: 11 cnt: 5
deconstruct of MutexLockGuard
construct of MutexLockGuard
args: 11 cnt: 4
deconstruct of MutexLockGuard
construct of MutexLockGuard
args: 11 cnt: 3
deconstruct of MutexLockGuard
construct of MutexLockGuard
args: 11 cnt: 2
deconstruct of MutexLockGuard
construct of MutexLockGuard
args: 11 cnt: 1
deconstruct of MutexLockGuard
construct of MutexLockGuard
deconstruct of MutexLockGuard
deconstruct of MutexLockGuard
deconstruct of MutexLockGuard
deconstruct of MutexLockGuard
deconstruct of MutexLock

When the four threads are created, four instances of MutexLockGuard are created. But why is the thread args: 11 destructed while the lifetime of other three threads are as long as the main thread?

tuzhi
  • 109
  • 8

3 Answers3

1

Your code is creating four threads.

Each of those threads enters the while(true) loop in f(), and therefore creates a MutexLockGuard. Then one of the threads enters the mutex, and execution continues.

So the sequence is

1) Four threads started, and all four create a MutexLockGuard. Hence the four lines construct of MutexLockGuard.

2) One of the threads (the first created according to your output) succeeds in locking the mutex, and produces the output args: 11 cnt: 5.

What this demonstrates is that locking a mutex is not necessarily instantaneous. The main() thread is possibly creating all threads before it gets preempted by any of its child threads.

Peter
  • 35,646
  • 4
  • 32
  • 74
1

All the threads construct a MutexLockGuard but only one is permitted to acquire the mutex and proceed (as intended).

However, when that one destroys its MutexLockGuard and releases the mutex, it turns out that it loops around and creates a new MutexLockGuard and acquires the mutex before the system unblocks another thread and allows them to acquire the mutex.

Mutex acquisition is not guaranteed to be fair. The system may act like this in an attempt to prevent spending work switching threads.

The moral of the story is to be careful not to assume that mutexes will order things. All that you should assume mutexes will do is prevent threads from executing at the same time.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
0

It is because when the thread is sleeping, it still holds the mutex. It only unlocks the mutex after it wakes up. So other threads do not have chance to get the mutex. Change the code as follow and try again

while(true){    
    if(cnt>0){
        usleep(1);
        {MutexLockGuard lock(mutex);
        cout << "args: " << t_num << " " << "cnt: " << cnt-- << endl; 
        }
    } 

    else{break;}       
}
Jonnus
  • 2,988
  • 2
  • 24
  • 33