Synchronization of linux multithreaded communication threads

mutex

Synchronization:
When multiple threads share the same memory, each thread needs to see the same attempt. When one thread modifies a variable, other threads can also read or modify the variable. Therefore, thread synchronization is required to ensure that they will not access invalid variables.

Mutex:
In a processor architecture where the variable modification time is longer than one memory access cycle, this potential inconsistency will occur when the read and write cycles of the memory intersect. Of course, this is related to the processor, but no preset can be made for the processor in a portable program.

mutex
In order to avoid midway data access by threads, variables need to be locked so that only one thread can access variables at the same time. The essence of mutex is lock.

  • After the mutex is locked, all threads that need to access the mutex will be blocked
  • After the mutex is unlocked, all the blocked threads become ready. The first thread to obtain the cpu will obtain the mutex and become running.

Mutex initialization;
1. dynamic allocation, pthread_mutex_init()
2. static allocation, set to constant pthread_ Mutex_ Initializer
3. the dynamically allocated mutex needs to call pthread before releasing memory_ Mutex_ Destroy()

  • int pthread_mutex_init(pthread_mutex_t restrict mutex, const pthread_mutexattr_t restrict attr);
    Parameter: initialized mutex; The property of the mutex, which is NULL by default

  • int pthread_mutex_destroy(pthread_mutex_t mutex);
    delete

  • int pthread_mutex_lock(pthread_mutex_t *mutex)// Lock
    0 is returned for success and error code is returned for failure. If it is locked, the thread will be blocked

  • int pthread_mutex_trylock(pthread_mutex_t *mutex)// Attempt to lock
    0 is returned for success and error code is returned for failure. If it is locked, the thread will be blocked

  • int pthread_mutex_unlock(pthread_mutex_t *mutex);
    0 is returned for success and error code is returned for failure

struct student
{
	
}stu;
int i;
pthread_mutex-t mutex;
void *thread_fun1(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);//Lock
       /*
       		Assign values to structural variables
		*/
        pthread_mutex_unlock(&mutex);//Unlock
	}
}
void *thread_fun2(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
       /*
       		Assign values to structural variables
		*/
        pthread_mutex_unlock(&mutex);
	}
}
int main()
{
	pthread_t tid1, tid2;
	int err;
	err =pthread_mutex_init(&mutex,NULL);//initialization
	pthread_jion(tid1,NULL);
	pthread_jion(tid2,NULL);
	pthread_mutex_destroy(&mutex);
}

In this way, when assigning values to structural variables, they will only be executed synchronously in one thread. Without locking, errors and confusion will occur.

Read write lock

Read / write locks are similar to mutexes, but mutexes are either locked or not, and only one thread is allowed to lock at the same time.
pthread_rwlock rwlock

The read / write lock has three states: lock in read mode, lock in write mode, and no lock. Only one thread can stand the read / write lock in even write mode at a time, but multiple threads can stand the read / write lock in read mode at the same time.

  • When a read / write lock is in the write lock state, all threads attempting to lock the lock will be blocked before it is unlocked
  • When reading a locked state, all threads that attempt to lock it in read mode gain access.
    But if a thread wants to lock it in write mode, it must block until all threads release the lock. Furthermore, read-write locks block subsequent read mode lock requests.

Read / write lock is very suitable for programs that read data structures more than write data. When it is locked in read mode, it is locked in shared mode; When it is locked in write mode, it is locked in exclusive mode.

initialization
int pthread_rwlock_init(pthread_rwlock_t restrict rwlock, const pthread_rwlockattr_t restrict attr);
Destroy after use
int pthread_rwlock_destroy(pthread_rwlock_t rwlock);

Read mode locking

  • int pyhread_rwlock_rdlock(pthread_rwlock_t *rwlock);
  • int pyhread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

Write mode locking

  • int pyhread_rwlock_wrlock(pthread_rwlock_t *rwlock);
  • int pyhread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

Unlock

  • int pyhread_rwlock_unlock(pthread_rwlock_t *rwlock);
pthread_rwlock_t rwlock;

void *thread_fun1(void *arg)
{
	while(1)
	{
		pthread_rwlock_wrlock(&rwlock);//Lock      
		printf("thread1 print num %d",num );
   		sleep(5);
   		printf("thread1 over\n");
        pthread_rwlock_unlock(&rwlock);//Unlock
	}
}
void *thread_fun2(void *arg)
{
	while(1)
	{
		pthread_rwlock_wrlock(&rwlock);
       printf("thread1 print num %d",num );      
       sleep(5);
       printf("thread1 over\n"); 
        pthread_rwlock_unlock(&rwlock);//Unlock
	}
}
int main()
{
	pthread_t tid1, tid2;
	int err;
	err =pthread_rwlock_init(rwlock,NULL);//initialization
	
	pthread_jion(tid1,NULL);
	pthread_jion(tid2,NULL);
	pthread_rwlock_destroy(&rwlock);
}

If the first operation is write locking, subsequent reads and writes must wait for write unlocking. If the first operation is read locking, subsequent reads and writes can be performed, and the read lock will be the subsequent write lock first.

Conditional variable

Condition variable is a mechanism to synchronize global variables shared among threads, which mainly includes two actions: a thread waits for "the condition of condition variable is established" and hangs; Another thread makes the condition true (gives a signal that the condition is true). To prevent contention, the use of conditional variables is always combined with a mutex.

pthread_cond_t cond

  • Static: pthread_ Cond_ T cond =pthread_ Cond_ Initializer
  • Dynamic: int pthread_ Cond_ Init (pthread\u cond\u t *restrict cond, const pthread\u condattr\restrict attr);
  • Int pthread_ Cond_ Destroy (pthread\u cond\u t *cond) // Destruction

Example:

#include "stdio.h"
#include "pthread.h"
#include "stdlib.h"
#include "signal.h"
#include "string.h"
#include "unistd.h"
#Define buffer_ Size 5 / / product inventory size
#define PRODUCT_CNT 	 30 / / total product production
struct product_cons
{
	int buffer[BUFFER_SIZE];
	pthread_mutex_t lock; //mutex 
	int readpos,writepos; //Read / write position
	pthread_cond_t notempty;  //Condition variable, non empty
	pthread_cond_t notfull; //Not full	
}buffer;
void init(struct product_cons *p)
{
	pthread_mutex_init(&p->lock,NULL);//mutex 
	pthread_cond_init(&p->notempty,NULL);//Condition variable - not empty
	pthread_cond_init(&p->notfull,NULL);//Condition variable not full
	p->readpos =0;//Start position is 0
	p->writepos =0;
}

void finish(struct product_cons *p)
{
	pthread_mutex_destroy(&p->lock);//mutex 
	pthread_cond_destroy(&p->notempty);  //Delete condition variable	
	pthread_cond_destroy(&p->notfull);
	p->readpos =0;   //0 at the end
	p->writepos =0;
}

void put(struct product_cons *p,int data)//Generate product to buffer
{
	pthread_mutex_lock(&p->lock);//Mutually exclusive lock to avoid confusion in reading and writing
	if((p->writepos+1)%BUFFER_SIZE==p->readpos)//Plus 1
	{
		printf("producer wait for full\n");
		pthread_cond_wait(&p->notfull, &p->lock);//Waiting is not full
	}
	p->buffer[p->writepos] =data;
	p->writepos ++; //Storage location +1
	if(p->writepos >=BUFFER_SIZE) //Zero when full.
		p->writepos =0;
	pthread_cond_signal(&p->notempty);//Issue non empty
	pthread_mutex_unlock(&p->lock);
}

int get(struct product_cons *p)
{
	int data;
	pthread_mutex_lock(&p->lock);
	if(p->readpos==p->writepos)//When reading catches up with writing, it means reading is over
	{
		printf("consumer wait for not empty\n");
		pthread_cond_wait(&p->notempty,&p->lock);//Wait for non empty to get
	}
	 data=p->buffer[p->readpos];
	 p->readpos++;
	 if(p->readpos>=BUFFER_SIZE)
	 	p->readpos=0;
	 pthread_cond_signal(&p->notfull); //Issue non full
	 pthread_mutex_unlock(&p->lock);
	 
	return data;
	
}

void *producer(void *data)//Produce one every 1 second
{
	int n;
	for(n=1;n<=50;++n)//Production of 50
	{
		sleep(2);
		printf("put the %d product...\n",n);
		put(&buffer,n);//Write n to buffer
		printf("put the %d product success\n",n);
	}
	printf("producer stopped\n");
	return NULL;
	
}

void *consumer(void *data) //Consume one every 2 seconds
{
	static int cnt =0;
	int num;
	while(1)
	{
		sleep(1);
		printf("get product...\n");
		num =get(&buffer);
		printf("get the %d product success\n",num);
		if(++cnt == PRODUCT_CNT) break; //Total 30
	}
	printf("consumer stopped\n");
	return NULL;
}

int main(int argc, char argv[])
{
	pthread_t th_a,th_b;
	void *retval;
	init(&buffer);
	pthread_create(&th_a,NULL,producer,0);
	pthread_create(&th_b,NULL,consumer,0);
	pthread_join(th_a,&retval);
	pthread_join(th_b,&retval);
	finish(&buffer); //Destroy mutex
	return 0;
	
}


Because the reading is once every 2 seconds and the output is once every 1 second, the setting cannot be put into the fifth one.

Tags: Linux Multithreading

Posted by Sayian on Tue, 31 May 2022 01:51:31 +0530