Operating System/System Programming(Arif Butt)

Lec31) Synchronization among Threads

Tony Lim 2021. 7. 25. 13:34
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void* f1(void * arg);
int main(int argc, char* argv[]){
   if(argc != 3){
      printf("Invalid number of arguments. Must pass two file names.\n");
      exit(1);
   }
   pthread_t tid1, tid2;
   void *rv1, *rv2;
   pthread_create(&tid1, NULL, f1, (void*)argv[1]);
   pthread_create(&tid2, NULL, f1, (void*)argv[2]);
   pthread_join(tid1, &rv1);
   pthread_join(tid2, &rv2);
   int count1 = *((int*)rv1);
   int count2 = *((int*)rv2);
   printf("Characters in %s: %d\n", argv[1], count1);
   printf("Characters in %s: %d\n", argv[2], count2);
   return 0;
}
void* f1(void* args){
   char* filename = (char*)args;
   int *result = (int*)malloc(sizeof(int));
   *result = 0;
   char ch;
   int fd = open(filename, O_RDONLY);
   while((read(fd, &ch, 1)) != 0){ 
	   (*result)++;
   }
   close(fd);
   pthread_exit((void*)result);
}

create 2 thread and join them to main thread.  remmber we need to compile this with "-lpthread -D_REENTRANT" 

Defining _REENTRANT causes the compiler to use thread safe (i.e. re-entrant) versions of several functions in the C library.

 

what data is shared

global data and static local data. The case of static local data is only significant if 2 or more threads execute the function containing static local variable at the same time.

dynamically allocated data in heap that has had its address put into a global/static variable

Data members of a class object that has 2 or more of its member functions caleld by different htreads at the same time.

 

what is not shared

Local variables are not shared. Even if 2 threads call the same function they will have different copies of the local variable in that function. This is because the local variables are kept on stack and every thread has its own stack.

Function parameters are not shared. In Languages like C, the parameters of function are also put on the stack & thus every thread will have its own copy of those as well

 

thread safe function

can be called simultaneously from multiple threads, even when the invocations use shared data. This is because each thread accesses shared data using some concurrency control mechanism

reentrant function

can also can be called simultaneously from multiple threads , but only if each invocation uses its own data

therefore thread-safe function is always reentrant, but a reentrant function is not always thread safe

 

 

Mutex

Mutual Exclusion device and is useful for protecting shared data strcutures from concurrent modifications, and implementing critical sections

A mutex has 2 possible states: unlocked (not owned by any thread) , and locked (owned by one thread). It can never be owned by 2 different threads simultaneously

A thread attempting to lock a mutex that is laready locked by another thread is suspended until the owner thread unlocks the mutex

Linux guarantees that race condition do not occur among threads attempting to lock a mutex

 

 

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void * f1(void * arg);
pthread_mutex_t mut;
int main(){
   pthread_mutexattr_t mutattr;
   pthread_mutexattr_init(&mutattr);
   pthread_mutexattr_settype(&mutattr, PTHREAD_MUTEX_ERRORCHECK_NP);
   pthread_mutex_init(&mut, &mutattr);
   pthread_t t1;
   pthread_create(&t1, NULL, f1,NULL);
   pthread_join(t1,NULL);
   printf("Bye Bye from main\n");
   return 0;
}
void * f1(void * arg){
   pthread_mutex_lock(&mut);
   pthread_mutex_lock(&mut);
   printf("Locking an already locked error checking mutex returns with error\n");
   pthread_mutex_unlock(&mut);
   pthread_exit(NULL);
}

to check thread error checking we need to use "pthread_mutexattr_t" and set attribute to our mutex ("mut")

when we execute blocking terminal will not happen and return with error.

_NP = non portable

 

 

 

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

pthread_mutex_t mutex;  
pthread_cond_t is_zero;   
long long  shared_data = 100000000;  
void* thread_function(void*);
int main (){
   pthread_t tid;
   pthread_cond_init(&is_zero, NULL);
   pthread_mutex_init(&mutex, NULL);
   pthread_create (&tid, NULL, thread_function, NULL);
//main thread waits till the shared data reaches zero
   pthread_mutex_lock(&mutex);
   if (shared_data != 0) 
      pthread_cond_wait(&is_zero, &mutex);
   pthread_mutex_unlock(&mutex);
    
   printf("shared_data = %lld \n", shared_data);
   pthread_mutex_destroy(&mutex);
   pthread_cond_destroy(&is_zero);
   return 0;
}
void* thread_function(void* arg){
   while(shared_data > 0){
      pthread_mutex_lock(&mutex);
      shared_data--;   
      pthread_mutex_unlock(&mutex);
   }
   pthread_cond_signal(&is_zero);
   return NULL;
}

main thraed checks if share_data is zero since it is not it goes to wait queue until the other thread signal() it with "pthread_cond_signal(&is_zero)" in this case "is_zero" is the queue.