[\<- Notes 02/21](02-21.md) --- # Threading Continued ## Locking - Sharing Variables - Requires mutual exclusion - Lock/unlock to avoid a race condition - Have one lock for each independent critical region - Bad Example (no synchronization): ``` int count = 0; void *update(){ //returns a pointer with no given data type int i; for(i = 0; i < 1000; i++){ count++; } } ``` - Good Example (using lock): ``` int count = 0; void *update(){ //returns a pointer with no given data type int i; for(i = 0; i < 1000; i++){ //lock count++; //unlock } } ``` - More real world example: ``` int balance = 100; //$100 //process 1: withdraw $20 //read balance (i.e $100) //write new balance (i.e $80) //process 2: withdraw $30 //read balance (i.e $80) //write new balance (i.e $50) ``` - In the above example, you need to use lock to avoid one process reading the balance before the other process finishes writing ## pthread library - We will use the Linux pthread library - Main information - `man pthread` - There are man pages for specific functions - Include ".h" file as shown in the man page: `#include ` - Compile using `-lpthread` - Operations: ``` #include pthread_t thread; int pthread_create( pthread_t *restrict thread, //thread id const pthread_attr_t *restrict attr, //attributes void *(*start_routine)(void *), //function void *restrict arg); //argument ``` - More operations: - `void pthread_exit(void *value_ptr);` - `int pthread_join(pthread_t thread, void **value_ptr);` - Example using pthreads - Alternating threads - Creates 3 threads - Let the system execute them in a round-robin fashion - Wait for them to finish at main ``` #include #include //this is the library that allows for threading void *loopThread(void *arg); int main(){ int i; pthread_t thr[3]; for(i = 0; i < 3; i++){ pthread_create(&thr[i], NULL, loopThread, (void *)i); } for(i = 0; i < 3; i++){ pthread_join(thr[i], NULL); } } void *loopThread(void *arg){ int i, j; int threadNo = (int)arg; //cast the void pointer for(i = 0; i < 20; i++){ printf("Thread %d\n", threadNo); sleep(1); } } ``` - Functions: ``` pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER //INIT int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); //LOCK int pthread_mutex_lock(pthread_mutex_t *mutex); //UNLOCK int pthread_mutex_unlock(pthread_mutex_t *mutex); ``` - Example using mutex lock: ``` //Using mutex lock //pthread_mutex_init called in main int count = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *update(){ int i; while(i = 0; i < 1000; i++){ pthread_mutex_lock(&mutex); count++; pthread_mutex_unlock(&mutex); } } ``` - Danger! - When one or more threads are waiting for one another and no thread can proceed - Example: - One thread is waiting for itself - Two threads are waiting for each other - Several threads are waiting for one another in a cycle ## Performance Considerations - Thread creation is expensive - Each thread has its own stack - Lock contention requires skills to keep many threads as possible running --- [-> Notes 03/04](03-04.md)