Darlyn
Darlyn

Reputation: 4938

Mutex and block scope

Having a mutex in code:

class Bank{
public:
    Bank():counter(0){};
    void addBankAccount(){
        unique_lock<mutex>  m ( g_Mtx );
        for( int i = 0; i < 1000; i++){
            counter++;
        }
    }

    int getCounter(){
        return counter;
    }

private:
    int counter;
    mutex g_Mtx;
};


int calculate(){
    Bank b;
    thread t1(&Bank::addBankAccount,&b);
    thread t2(&Bank::addBankAccount,&b);
    t1.join();
    t2.join();
    if( b.getCounter() != 2000)
        cout << "Error value is " << b.getCounter() << endl;
}

int main()
{
    for(int i = 0; i < 10000; i++)
        calculate();
    return 0;
}

the mutex itself is neccessery , in order to maintain right result. If i am not mistaken without mutex it would result in something:

      THREAD 1                           |              THREAD 2
                                         | 
 1)  load value of counter in register   |  2) load value of counter in register
     value in register is 0              |     value in register is 0

 3)   increment register( value is 1)    |  4)    increment register( value is 1)

4) update variable, value of             |  5) update variable, value of
   counter is set to 1                          again counter is set to 1

( marking 1)... 5) should indicate in what order can be instruction processed by computer ) Thus some increments are omitted. But my question is about mutex itself.

When two threads are running for example same function

void addBankAccount(){
    unique_lock<mutex>  m ( g_Mtx );
    for( int i = 0; i < 1000; i++){
        counter++;
    }
}

In my undertanding , when mutex is declared and lock is being locked , the thread that wants to acces to data that is held in lock "pauses" and "resumes" when the lock is unlocked and it can access the data ( the threads that wants to use that data are in queue ). But what happens when mutex is in another block scope? For example :

    void addBankAccount(){
        {
           unique_lock<mutex>  m ( g_Mtx );
           for( int i = 0; i < 1000; i++){
             counter++;
        }
         // some other piece of code
    }

Would this change something? I have seen this usage and i do not see how it differs from original declaration of the function. When thread want to access data that are locked , does it "pauses" whole thread , or does it "pauses" just the block scope , so in this case it would jumpe to

 // some other piece of code

and when lock is unlocked it jumps back in loop inside scope?

I have read quite a few articles about mutex but i am still confused with this.

And ofc , my understanding of mutex can be wrong.

All explanations are appreciated.

Thanks

Upvotes: 0

Views: 2903

Answers (1)

In your example, there is no difference. The reason to embed the lock in an inner block, is so that the mutex can be released early..

void addBankAccount(){
    int count_save;
    {
       unique_lock<mutex>  m ( g_Mtx );
       for( int i = 0; i < 1000; i++){
         counter++;
       }
       count_save = counter;
     }  // mutex will be released here

     // Still inside addBankAccount, but now it's safe to do IO
     std::cout << "Bank account is now" << count_saved;
}

You often find you want to release a mutex, and then do some other processing. For example, IO is slow, and involves obtaining other locks; it's a good idea not to do it while holding a mutex.

Notice that in the example, count_save is a local variable and as such is private to the thread - it is not part of the shared state being protected by the mutex. It is essential not to touch any of the shared state once the mutex has been released.

Note also, that because of the lack of synchronization, the account state can have changed before the output occurs. In fact, there is no guarantee that the output from an earlier change will occur before the output from a later change. (As such, this may not have been a particularly good example!)

Upvotes: 1

Related Questions