Mutual Exclusive (Mutex) Locks

  • A mutex lock synchronization object has 2 states:

    • Unlocked
    • Locked

  • A mutex lock synchronization object has 2 operations:

    • Unlock(mutex x):

      • If x is in the locked state, the state is changed to unlocked
      • If x is in the unlocked state, the operation has no effect

    • Lock(mutex x):

      • If x is unlocked, Lock(mutex x) will return and the state of x is changed to locked
      • If x is locked, Lock(mutex x) will block until x is changed to unlocked

Creating Mutex locks in PThread

  • Defining a mutex lock "object" in PThread:

       pthread_mutex_t  x;  // Define a mutex lock named x
    

  • Initializing a mutex lock "object" in PThread:

       int pthread_mutex_init(pthread_mutex_t     *mutex,
    			  pthread_mutexattr_t *attr   );
    
           initializes the mutex "object" mutex with attributes attr
    
           When attr = NULL: use default (state = UNLOCKED)
    

  • Example: creating a mutex lock with initial state = UNLOCKED

       pthread_mutex_t x;             /* Define a mutex lock "x" */
    
       pthread_mutex_init(&x, NULL);  /* Initialize "x" to UNLOCKED */  
    

Locking and Unlocking a Mutex Lock in PThread

  • Locking a Mutex lock in PThread:

      int pthread_mutex_lock(pthread_mutex_t *mutex);
    
         locks the mutex lock "mutex"
    
      Example:
    
          pthread_mutex_t x;
    
          pthread_mutex_init(&x, NULL);
          ...
          pthread_mutex_lock(&x);      
    

  • Unlocking a Mutex lock in PThread:

      int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
         unlocks the mutex lock "mutex"
    
      Example:
    
          pthread_mutex_unlock(&x);      
    

Using mutex lock to synchronize access of shared variables

  • A most common application of mutex is:

    • Synchronize updates of shared (global) variables

  • Reconsider the concurrent updates to a shared variable N by 2 threads:

    int N = 0;
    
    void *worker(void *arg)
    {
        for ( int i = 0; i < 100000; i++)
            N++;
    }
    
    int main(int argc, char *argv[])
    {
        pthread_t tid;
        pthread_create(&tid, NULL, worker, NULL);
    
        for ( int i = 0; i < 100000; i++)
            N++;
    
        pthread_join(tid, NULL);
        printf("Final value: N = %d\n", N);
    }

DEMO: demo/pthread/mutex1a.c

Using mutex lock to synchronize access of shared variables

  • We will use a mutex lock to synchronize the concurrent updates to a shared variable N:

    int N = 0;
    pthread_mutex_t  mutex_N;       // Protect N from concurrent updates
    
    void *worker(void *arg)
    {
        for ( int i = 0; i < 100000; i++) {
    
            N++;
    
        }
    }
    
    int main(int argc, char *argv[])
    {
        pthread_t tid;
    
    
        pthread_create(&tid, NULL, worker, NULL);
        
        for ( int i = 0; i < 100000; i++) {
    
            N++;
    
        }
    
        pthread_join(tid, NULL);
        printf("Final value: N = %d\n", N);
    }

 

Using mutex lock to synchronize access of shared variables

  • Initial the mutex lock before creating any threads:

    int N = 0;
    pthread_mutex_t  mutex_N;       // Protect N from concurrent updates
    
    void *worker(void *arg)
    {
        for ( int i = 0; i < 100000; i++) {
    
            N++;
    
        }
    }
    
    int main(int argc, char *argv[])
    {
        pthread_t tid;
    
        pthread_mutex_init(&mutex_N, NULL); // Initial mutex_N
        pthread_create(&tid, NULL, worker, NULL);
        
        for ( int i = 0; i < 100000; i++) {
    
            N++;
    
        }
    
        pthread_join(tid, NULL);
        printf("Final value: N = %d\n", N);
    }

 

Using mutex lock to synchronize access of shared variables

  • Lock the mutex before you update the shared variable:

    int N = 0;
    pthread_mutex_t  mutex_N;       // Protect N from concurrent updates
    
    void *worker(void *arg)
    {
        for ( int i = 0; i < 100000; i++) {
            pthread_mutex_lock(&mutex_N);
            N++;
    
        }
    }
    
    int main(int argc, char *argv[])
    {
        pthread_t tid;
    
        pthread_mutex_init(&mutex_N, NULL); // Initial mutex_N
        pthread_create(&tid, NULL, worker, NULL);
        
        for ( int i = 0; i < 100000; i++) {
            pthread_mutex_lock(&mutex_N);
            N++;
    
        }
    
        pthread_join(tid, NULL);
        printf("Final value: N = %d\n", N);
    }

 

Using mutex lock to synchronize access of shared variables

  • Unlock the mutex after you update the shared variable:

    int N = 0;
    pthread_mutex_t  mutex_N;       // Protect N from concurrent updates
    
    void *worker(void *arg)
    {
        for ( int i = 0; i < 100000; i++) {
            pthread_mutex_lock(&mutex_N);
            N++;
            pthread_mutex_unlock(&mutex_N);
        }
    }
    
    int main(int argc, char *argv[])
    {
        pthread_t tid;
    
        pthread_mutex_init(&mutex_N, NULL); // Initial mutex_N
        pthread_create(&tid, NULL, worker, NULL);
        
        for ( int i = 0; i < 100000; i++) {
            pthread_mutex_lock(&mutex_N);
            N++;
            pthread_mutex_unlock(&mutex_N);
        }
    
        pthread_join(tid, NULL);
        printf("Final value: N = %d\n", N);
    }

DEMO: demo/pthread/mutex1b.c

Deadlock...

  • Warning:

    • Make sure you unlock a mutex after you are done with accessing the share variable(s).

  • Deadlock:

    • If you forget to unlock a mutex, the program can become deadlocked

    (Because the mutex will remain locked so all further pthread_mutex_lock( ) operations will block)

DEMO: demo/pthread/mutex1c.c