linux-多线程通信(三)线程的同步

互斥量

同步:
当多个线程共享相同的内存时,需要每一个线程看到相同的试图,当一个线程修改变量时,其他线程也可以读取或修改这个变量,就需要线程的同步,确保他们不会访问到无效的变量。

互斥量:
在变量修改时间多于以一个存储器访问周期的处理器结构中,当存储器的读和写这两个周期交叉时,这种潜在的不一致性就会出现,当然这与处理器相关,但是在可移植的程序中不能对处理器做出任何预设。

互斥锁
为了让线程访问数据不产生中途,需要对变量加锁,使得同一时刻只有一个线程可以访问变量,互斥量的本质就是锁。

  • 互斥量加锁后,所有需要访问该互斥量的线程都将阻塞
  • 互斥量解锁后,所有因此阻塞的线程都变成就绪态,第一个获得cpu的线程会获得互斥量变为运行态。

互斥量初始化;
1.动态分配,pthread_mutex_init()
2.静态分配,设置为常量PTHREAD_MUTEX_INTIALIZER
3.动态分配的互斥量在释放内存之前需要调用pthread_mutex_destroy()

  • int pthread_mutex_init(pthread_mutex_t restrict mutex, const pthread_mutexattr_t restrict attr);
    参数:初始化的互斥量 ; 互斥量的属性,默认为NULL

  • int pthread_mutex_destroy(pthread_mutex_t mutex);
    删除

  • int pthread_mutex_lock(pthread_mutex_t *mutex); //加锁
    成功返回0 ,失败返回错误码。如果是已经被锁住的,导致该线程阻塞

  • int pthread_mutex_trylock(pthread_mutex_t *mutex); //试图加锁
    成功返回0 ,失败返回错误码。如果是已经被锁住的,导致该线程阻塞

  • int pthread_mutex_unlock(pthread_mutex_t *mutex);
    成功返回0 ,失败返回错误码

struct student
{
	
}stu;
int i;
pthread_mutex_t mutex;
void *thread_fun1(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);//加锁
       /*
       		对结构体变量赋值
		*/
        pthread_mutex_unlock(&mutex);//解锁
	}
}
void *thread_fun2(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
       /*
       		对结构体变量赋值
		*/
        pthread_mutex_unlock(&mutex);
	}
}
int main()
{
	pthread_t tid1, tid2;
	int err;
	err =pthread_mutex_init(&mutex,NULL);//初始化
	pthread_jion(tid1,NULL);
	pthread_jion(tid2,NULL);
	pthread_mutex_destroy(&mutex);
}

这样对结构体变量赋值时,只会同步在一个线程中执行,不加锁会产生错错乱。

读写锁

读写锁与互斥量类似,不过互斥量要么加锁要么不加,而且同一时刻只允许一个线程加锁。
pthread_rwlock rwlock

读写锁有三种状态,读模式下加锁,写模式下加锁,不加锁,一次只有一个线程可以站偶写模式下的读写锁,但是多个线程可以同时站有读模式的读写锁。

  • 读写锁在写加锁状态时,在它被解锁之前,所有试图对这个锁加锁的线程都会阻塞
  • 在读加锁状态时,所有试图以读模式对其加锁的线程都会获得访问权。
    但是如果线程希望以写模式对其加锁,它必须阻塞到所有线程释放锁。而且读写锁会阻塞随后的读模式锁请求。

读写锁非常适合对数据结构读次数大于写次数的程序,当它以读模式锁住时,是以共享的方式锁住的;当它以写模式锁住时,是以独占的模式锁住的。

初始化
int pthread_rwlock_init(pthread_rwlock_t restrict rwlock, const pthread_rwlockattr_t restrict attr);
使用完需要销毁
int pthread_rwlock_destroy(pthread_rwlock_t rwlock);

读模式加锁

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

写模式加锁

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

解锁

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

void *thread_fun1(void *arg)
{
	while(1)
	{
		pthread_rwlock_wrlock(&rwlock);//加锁      
		printf("thread1 print num %d",num );
   		sleep(5);
   		printf("thread1 over\n");
        pthread_rwlock_unlock(&rwlock);//解锁
	}
}
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);//解锁
	}
}
int main()
{
	pthread_t tid1, tid2;
	int err;
	err =pthread_rwlock_init(rwlock,NULL);//初始化
	
	pthread_jion(tid1,NULL);
	pthread_jion(tid2,NULL);
	pthread_rwlock_destroy(&rwlock);
}

如果先运行的是写加锁,后续无论读写都要等写解锁,若识先运行读加锁,后续读写都能,且读锁会为后续写锁先行。

条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

pthread_cond_t cond

  • 静态:pthread_cond_t cond =PTHREAD_COND_INTIALIZER
  • 动态:int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
  • int pthread_cond_destroy(pthread_cond_t *cond) //销毁

例:

linux-多线程通信(三)线程的同步_第1张图片
#include "stdio.h"
#include "pthread.h"
#include "stdlib.h"
#include "signal.h"
#include "string.h"
#include "unistd.h"
#define BUFFER_SIZE   5 //产品库存大小
#define PRODUCT_CNT	30  //产品生产总数
struct product_cons
{
	int buffer[BUFFER_SIZE];
	pthread_mutex_t lock; //互斥锁
	int readpos,writepos; //读写位置
	pthread_cond_t notempty;  //条件变量,非空
	pthread_cond_t notfull; //非满	
}buffer;
void init(struct product_cons *p)
{
	pthread_mutex_init(&p->lock,NULL);//互斥锁
	pthread_cond_init(&p->notempty,NULL);//条件变量-非空
	pthread_cond_init(&p->notfull,NULL);//条件变量非满
	p->readpos =0;//起始位置都为0
	p->writepos =0;
}

void finish(struct product_cons *p)
{
	pthread_mutex_destroy(&p->lock);//互斥锁
	pthread_cond_destroy(&p->notempty);  //删除条件变量	
	pthread_cond_destroy(&p->notfull);
	p->readpos =0;   //结束都归0
	p->writepos =0;
}

void put(struct product_cons *p,int data)//生产品到buffer
{
	pthread_mutex_lock(&p->lock);//互斥锁,避免读写混乱
	if((p->writepos+1)%BUFFER_SIZE==p->readpos)//如9+1%5=0 说明刚取走第四个,放入第九个需要等待
	{
		printf("producer wait for full\n");
		pthread_cond_wait(&p->notfull, &p->lock);//等待非满
	}
	p->buffer[p->writepos] =data;
	p->writepos ++; //存储位置+1
	if(p->writepos >=BUFFER_SIZE) //满了 归零。
		p->writepos =0;
	pthread_cond_signal(&p->notempty);//发出非空
	pthread_mutex_unlock(&p->lock);
}

int get(struct product_cons *p)
{
	int data;
	pthread_mutex_lock(&p->lock);
	if(p->readpos==p->writepos)//读追上了写,说明读完
	{
		printf("consumer wait for not empty\n");
		pthread_cond_wait(&p->notempty,&p->lock);//阻塞当前线程,等待put发送非空信号才能获取
	}
	 data=p->buffer[p->readpos];
	 p->readpos++;
	 if(p->readpos>=BUFFER_SIZE)
	 	p->readpos=0;
	 pthread_cond_signal(&p->notfull); //发出非满
	 pthread_mutex_unlock(&p->lock);
	 
	return data;
	
}

void *producer(void *data)//每隔1秒生产一个
{
	int n;
	for(n=1;n<=50;++n)//生产50个
	{
		sleep(2);
		printf("put the %d product...\n",n);
		put(&buffer,n);//把n写到buffer里
		printf("put the %d product success\n",n);
	}
	printf("producer stopped\n");
	return NULL;
	
}

void *consumer(void *data) //每隔2秒消费一个
{
	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; //计满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); //销毁互斥量
	return 0;
	
}

linux-多线程通信(三)线程的同步_第2张图片
因为读是2秒一次,产出是1秒一次,设定是放不进第五个。

你可能感兴趣的:(linux-多线程通信(三)线程的同步)