130 likes | 237 Views
Synchronizing threads with mutexes. I T 421 Operating system Chapter6 . Synchronizing threads with mutexes. Threads can greatly simplify writing elegant and efficient programs.
E N D
Synchronizing threads with mutexes IT421 Operating system Chapter6
Synchronizing threads with mutexes • Threads can greatly simplify writing elegant and efficient programs. • However, there are problems when multiple threads share a common address space, like the variable data in next example.
Example • To understand what might happen, consider the following code: THREAD 1 THREAD 2 a = data; b = data; a++; b--; data = a; data = b; • Now if this code is executed serially (for instance, THREAD 1 first and then THREAD 2), there are no problems. • However threads execute in an arbitrary order, so consider the following situation:
Example THREAD 1 THREAD 2 a = data; b = data; a++; b--; data = a; data = b; • [data = data - 1!!!!!!!] • So data could end up +1, 0, -1, and there is NO WAY to know which value! It is completely non-deterministic!
Solution • The solution to this is to provide functions that will block a thread if another thread is accessing data that it is using. • Pthreads use a data type called a mutex to achieve this.
What is a mutex? • A basic mechanism supplied by the pthreads library to solve the data race problem, is called a mutex. • Mutexeshave two basic operations, lock and unlock. If a mutex is unlocked and a thread calls lock, the mutex locks and the thread continues. If however the mutex is locked, the thread blocks until the thread holding the lock calls unlock. • Locking a mutex is an atomic operation, meaning that the operating system (or threads library) assures you that if you locked a mutex, no other thread succeeded in locking this mutex at the same time.
Creating and initializing a mutex • In order to create a mutex, we first need to declare a variable of type pthread_mutex_t and then initialize it using the function intpthread_mutex_init (pthread_mutex_t *mut, constpthread_mutexattr_t *attr); • The first argument is a pointer to the mutex. To second argument is used to set the mutex attributes. To use the default mutex attributes, just pass NULL to it. • example: pthread_mutex_ta_mutex; pthread_mutex_init(&a_mutex, NULL);
Locking and unlocking a mutex • In order to lock a mutex, we may use the function pthread_mutex_lock(). This function attempts to lock the mutex, or block the thread if the mutex is already locked by another thread. In this case, when the mutex is unlocked by the first thread, the function will return with the mutex locked by our thread. Here is how to lock a mutex (assuming it was initialized earlier):
Example intrc = pthread_mutex_lock(&a_mutex); if (rc) { /* an error has occurred */ perror("pthread_mutex_lock"); pthread_exit(NULL); } /* mutex is now locked - do your stuff. */ After the thread did what it had to (change variables or data structures, handle file, or whatever it intended to do), it should free the mutex, using the pthread_mutex_unlock() function, like this: rc = pthread_mutex_unlock(&a_mutex); if (rc) { perror("pthread_mutex_unlock"); pthread_exit(NULL); }
Destroying a mutex • After we finished using a mutex (that is, no thread needs it at all), we should destroy it. However, if only one thread finished with the mutex, it should leave it alive for the other threads that might still need to use it. Once all finished using it, the last one can destroy it using the pthread_mutex_destroy() function: • rc = pthread_mutex_destroy(&a_mutex); • After this call, this variable (a_mutex) may not be used as a mutex any more, unless it is initialized again. Thus, if one destroys a mutex too early, and another thread tries to lock or unlock it, that thread will get an error from the lock or unlock function.
Using a mutex - a short example Consider the problem we had before and now let us use mutexes: THREAD 1 THREAD 2 pthread_mutex_lock (&a_mutex); pthread_mutex_lock(&a_mutex); a = data; /* blocked */ a++; /* blocked */ data = a; /* blocked */ pthread_mutex_unlock (&a_mutex); /* blocked */ b = data; b--; data = b; pthread_mutex_unlock(&a_mutex); [data is fine. The data race is gone.]
Using a mutex - a short exampleConsider the problem we had before and now let us use mutexes: THREAD 1 pthread_mutex_lock(&a_mutex); a = data; /* blocked */ a++; /* blocked */ data = a; /* blocked */ pthread_mutex_unlock(&a_mutex); THREAD 2 pthread_mutex_lock(&a_mutex); b = data; b--; data = b; pthread_mutex_unlock(&a_mutex); [data is fine. The data race is gone.]
Let’s test then modify the thread_add.c program on Assignment7