Thread control: thread synchronization (including reader writer problems) and (producer consumer problems)

Thread synchronization:

1. Method of process synchronization
2. The difference of mutex, read-write lock and condition variable
3. Reasonable synchronization to avoid deadlock

<1> mutual exclusion
1) Why use mutually exclusive variables?

Example 1: when the same shared variable (resource) is called between different threads without mutual exclusion, its value cannot be guaranteed. In the following example, the cross calling of global shared variable i by thread 1 and thread 2 will result in different output results;

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
typedef struct Mutex{
        int mux_1;
        int mux_2;
  }Mutex;
  int i;//Global shared variables
  Mutex Mt;//Mutually exclusive variables
  void *pthread_fun1(void *arg)
  {
    while(1)
    {
        Mt.mux_1=i;
        Mt.mux_2=i;
        if(Mt.mux_1 !=Mt.mux_2)
        {
        printf("%s Mt.mux_1=%d   Mt.mux_2=%d\n",arg,Mt.mux_1,Mt.mux_2);
        break;
        }
        ++i;
    }
 return (void*)1;
 }
 
 void *pthread_fun2(void *arg)
  {
    while(1)
    {
        Mt.mux_1=i;
        Mt.mux_2=i;
        if(Mt.mux_1 !=Mt.mux_2)
        {
        printf(" %s Mt.mux_1=%d   Mt.mux_2=%d\n",arg,Mt.mux_1,Mt.mux_2);
        break;
        }
        ++i;
    }
 return (void*)1;
 }
 
 int main()
 {
        pthread_t tid1,tid2;
        int err1,err2;
        int ini_mux;
        
        err1=pthread_create(&tid1,NULL,pthread_fun1,"thread1");
        if(err1 !=0)
        {
        printf("create new thread1\n");
        return -1;
        }
        err2=pthread_create(&tid2,NULL,pthread_fun2,"thread2");
        if(err2 !=0)
        {
        printf("create new thread2\n");
        return -2;
        }
        
        pthread_join(tid1,NULL);//Wait for the new thread to finish executing
        pthread_join(tid2,NULL);//Wait for the new thread to finish executing
        
        return 0;
 }

The output results are shown in the figure:

The value of i (shared resource) without mutex will change randomly in the call of any different thread.
2) Initialization and destruction of mutex.

3) Lock and unlock

4) Mutex instance

Example 2: Based on example 1, build mutual exclusion for shared resource i to ensure that i will not be preempted by other threads when one thread calls. Create the file pthread? Mutex? T.C; the content is as follows

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
typedef struct Mutex{
        int mux_1;
        int mux_2;
  }Mutex;
  int i;//Global shared variables
  Mutex Mt;//Structure object
  pthread_mutex_t mutex;//mutex
  void *pthread_fun1(void *arg)
  {
    while(1)
    {
        pthread_mutex_lock(mutex);//p operation locks i resources to ensure that threads are not preempted before they are used up
        Mt.mux_1=i;
        Mt.mux_2=i;
        if(Mt.mux_1 != Mt.mux_2)
        {
        printf("%s Mt.mux_1=%d  Mt.mux_2=%d\n",arg,Mt.mux_1,Mt.mux_2);
        }
        ++i;
       pthread_mutex_unlock(mutex);//Unlock and release i resources to ensure that other threads can use
    }
 return (void*)1;
 }
 
 void *pthread_fun2(void *arg)
  {
  pthread_mutex_lock(mutex);
    while(1)
    {
        pthread_mutex_lock(mutex);//p operation locks i resources to ensure that threads are not preempted before they are used up
        Mt.mux_1=i;
        Mt.mux_2=i;
         if(Mt.mux_1 != Mt.mux_2)
        {
        printf(" %s Mt.mux_1=%d   Mt.mux_2=%d\n",arg,Mt.mux_1,Mt.mux_2);
        }
        ++i;
       pthread_mutex_unlock(mutex);//Unlock and release i resources to ensure that other threads can use
    }
    }
 return (void*)1;
 }
 
 int main()
 {
        pthread_t tid1,tid2;
        int err1,err2;
        int ini_mux;
        //Initialize the mutex so that the mutex is available
        ini_mux=pthread_mutex_init(metux);
        if(ini_mux !=0)
        {
        printf(" inital mutex failure\n");
        return ini_mux;
        }
        
        err1=pthread_create(&tid1,NULL,pthread_fun1,"thread1");
        if(err1 !=0)
        {
        printf("create new thread1\n");
        return -1;
        }
        err2=pthread_create(&tid2,NULL,pthread_fun2,"thread2");
        if(err2 !=0)
        {
        printf("create new thread2\n");
        return -2;
        }
        
        pthread_join(tid1,NULL);//Wait for the execution of the new thread to finish
        pthread_join(tid2,NULL);//Wait for the execution of the new thread to finish
        
        return 0;
        }
        
        

The operation results are as follows:

As shown in the figure, no data is printed out, which means that after the mutex metux is set, i resources will not be preempted and called by other threads when one thread calls, so the condition of mux_! = mux_willnever be true. The results will not be printed.
5) Anatomy of deadlock




<2> Reading and writing lock (reader writer problem)
1) What is a read-write lock? The difference between it and an exclusive lock

2) Initialization and destruction of read / write lock:

3) Shackles and unlocks:

4) Read write lock instance:

Example 3: verify that a shared resource can be read and locked by multiple threads at the same time, that is, multiple read and lock at the same time.

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
pthread_rwlock_t rwlock;//Global read write lock object
  void *pthread_fun1(void *arg)
  {
   
      pthread_rwlock_rdlock(&rwlock);//Add read mode lock
     printf("new thread 1\n");//Printing
    sleep(5);//Sleep for 5 seconds and give up cpu
    printf("here is thread 1 exit\n");
   pthread_rwlock_unlock(&rwlock);//Unlock
 return (void*)1;
 }
 
 void *pthread_fun2(void *arg)
  {
      pthread_rwlock_rdlock(&rwlock);//Add read mode lock
     printf(" new thread 2\n");
    sleep(5);//Sleep for 5 seconds and give up cpu
    printf("here is thread 2 exit\n");
   pthread_rwlock_unlock(&rwlock);//Unlock
 return (void*)1;
 }
 
 int main()
 {
        pthread_t tid1,tid2;
        int err1,err2;
        int er_lock;
        
        er_lock=pthread_rwlock_init(&rwlock,NULL);//Initialization
        if(er_lock !=0)
        {
        printf("init rwlock failure\n");
        return er_lock;
        }
        
        err1=pthread_create(&tid1,NULL,pthread_fun1,"thread1");
        if(err1 !=0)
        {
        printf("create new thread1\n");
        return -1;
        }
        err2=pthread_create(&tid2,NULL,pthread_fun2,"thread2");
        if(err2 !=0)
        {
        printf("create new thread2\n");
        return -2;
        }
        
        pthread_join(tid1,NULL);//Wait for the new thread to finish executing
        pthread_join(tid2,NULL);//Wait for the new thread to finish executing
        
        return 0;
 }

The operation results are as follows:

As shown in the figure, new thread 2 and new thread 1 are printed out at the same time, which proves that a shared resource variable can be read and locked by multiple threads at the same time.
Example 4: Based on example 3, change the read-locked pthread ﹣ rwlock ﹣ rdlock in the new thread to write locked pthread ﹣ rwlock ﹣ rdlock, and run the compilation. The results are as follows:

As shown in the figure, thread 2 can read lock first and then write unlock, then thread 1 can write lock and then start writing.
Write lock and read lock are mutually exclusive. They can't be read when writing and can't be written when reading.
<3> Conditional variables (producer and consumer issues)
1) Introduction of conditional variable

2) Initialization and destruction of conditional variables

3) Use of conditional variables

4) Conditional variable instances (producers and consumers)

Create the file phtread · producer · consmer. C; the contents are as follows

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"

#Define buffer_size 5 / / product inventory size
#Define product? CNT 30 / / total production

typedef struct product_cons {
    int buffer[BUFFER_SIZE];//yield a product
    pthread_mutex_t lock;//Mutex volatile int
    int readpos,writepos;//Location of reading and writing
    pthread_cond_t notempty;//Conditional variable, non empty
    pthread_cond_t notfull;//Conditional variable, not full
    }Buffer;
    Buffer buffer;//Global object
    void init(Buffer *p)
    {
        pthread_mutex_init(&p->lock,NULL);//Initialize mutex
        pthread_cond_init(&p->notempty,NULL);//Initialization condition variable
        pthread_cond_init(&p->notfull,NULL);//Initialization condition variable
        p->readpos =0;//Initialize read location
        p->writepos =0;//Initialize write location
    }
    void finish(Buffer *p)
    {
        pthread_mutex_destroy(&p->lock);//Destroy mutex
        pthread_cond_destroy(&p->notempty);//Destroy condition variable
        pthread_cond_destroy(&p->notfull);//Destroy condition variable
        p->readpos =0;//Zero reading position
        p->writepos =0;//Zero write position
    }
    //Store a data to buffer
    void put(Buffer *p,int data)//Production function (team entry)
    {
        
        pthread_mutex_lock(&p->lock);//Add write mode lock before writing
        if((p->writepos+1)%BUFFER_SIZE==p->readpos)
        {
        printf("producer wait for not full\n");
        pthread_cond_wait(&p->notfull,&p->lock);
        }
        p->writepos=(p->writepos+1)%BUFFER_SIZE;
        p->buffer[p->writepos]=data;
        
        pthread_cond_signal(&p->notempty);
        pthread_mutex_unlock(&p->lock);
        
    }
    
    //Read, remove a data from the buffer
    int get(Buffer *p)//Consumer product function (outbound)
    {
       int data;
        pthread_mutex_lock(&p->lock);//Add read mode lock before reading
        
        if(p->readpos == p->writepos)//Team empty
        {
        printf("consumer wait for not empty\n");
        pthread_cond_wait(&p->notempty,&p->lock);
    }
    p->readpos=(p->readpos+1)%BUFFER_SIZE;
    data = p->buffer[p->readpos];
    
    pthread_cond_signal(&p->notfull);
    pthread_mutex_unlock(&p->lock);
    
    return  data;;
  }
    void *producer(void *data)//Child thread, producer
    {
        int n;
        for(n=1;n<=50;++n)//Produce 50 products
        {
            sleep(1);
            printf("producer put the %d product ... \n",n);
            put(&buffer,n);
            printf("producer put the %d product success\n",n);
        }
        
        printf("producer stopped");
        
        return NULL;
    }
    
    void *consumer(void *data)
    {
        static int cnt =0;
        int num;
        while(1)
        {
            sleep(2);
            printf("consumer get product ...\n");
            num = get(&buffer);
            printf("consumer get the %d product success\n",num);
            if(++cnt == PRODUCT_CNT)
                break;
        }
        printf("consumer stopped\n");
        return NULL;
      }
    int main(int argc,char *argv[])
    {
        pthread_t th_a,th_b;
        void *rval;
        
        init(&buffer);
        
        pthread_create(&th_a,NULL,producer,0);
        pthread_create(&th_b,NULL,consumer,0);
        
        pthread_join(th_a,&rval);
        pthread_join(th_b,&rval);
        
        finish(&buffer);
        
        return 0;
    }

        

The operation results are as follows:

Published 32 original articles, won praise 11, visited 1393
Private letter follow

Posted on Tue, 14 Jan 2020 22:47:10 -0800 by dtdetu