linux系统(线程同步)09_线程同步,互斥量,死锁,读写锁,条件变量介绍和生产者和消费者模型及实现,信号量,信号量的生产者和消费者模型及实现,文件锁单开进程,哲学家就餐模型分析

01 学习目标

1.熟练掌握互斥锁的使用
2.说出什么叫死锁以及解决方案
3.熟练掌握读写锁的使用
4.熟练掌握条件变量的使用
5.理解条件变量实现的生产消费者模型
6.理解信号量实现的生产消费者模型

02 线程同步的概念

同步的概念:如文件同步,指让两个或多个文件夹里的文件保持一致,等等。
但线程同步不一致。
同步即协同步调,按预定的先后次序运行。
线程同步:指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其他线程为保证数据一致性,不能调用该功能。
在这里插入图片描述

解决同步问题:协调步骤,顺序执行。
解决同步的问题:加锁!

数据混乱的原因:

  1. 资源共享(独享资源则不会)
  2. 调度随机(意味着数据访问会出现竞争)
  3. 线程间缺乏必要的同步机制
    只能解决第三点。

03 互斥量的使用

两个线程访问同一块共享资源,如果不协调顺序,容易造成数据混乱。

加锁
mutex

pthread_mutex_intit 初始化
pthread_mutex_destroy 摧毁
pthread_mutex_lock 加锁
pthread_mutex_unlock(pthread_mutex_t * mutex) 解锁

互斥量的使用步骤:

  • 初始化
  • 加锁
  • 执行逻辑–操作共享数据
  • 解锁

注意事项:
加锁需要最小粒度,不要一直占用临界区。

pthread_print.c:

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

pthtread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int sum=0;

void *thr1(void *arg)
{
	while(1)
	{
		//先上锁
		pthread_mutex_lock(&mutex);//加锁当有线程已经加速的时候会阻塞
		printf("hello");
		sleep(rand()%3);
		printf("world\n");
		//释放锁
		pthread_mutex_unlock(&mutex);
		sleep(rand()%3);
	}
}

void *thr2(void *arg)
{
	while(1)
	{
		//先上锁
		pthread_mutex_lock(&mutex);//加锁当有线程已经加速的时候会阻塞
		printf("HELLO");
		sleep(rand()%3);
		printf("WORLD\n");
		//释放锁
		pthread_mutex_unlock(&mutex);
		sleep(rand()%3);
	}
}

int main()
{
	pthread_t tid[2];
	pthread_create(&tid[0],NULL,thr1,NULL);
	pthread_create(&tid[1],NULL,thr2,NULL);
	
	pthread_join(tid[0],NULL);
	pthread_join(tid[1],NULL);
	return 0;
}

输出效果:要么全大写,要么全小写。
在这里插入图片描述
尝试加锁

int pthread_mutex_lock(pthread_mutex_t *mutex);

mutex_trylock.c:

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

pthtread_mutex_t mutex ;

void *thr(void *arg)
{
	while(1)
	{
		//先上锁
		pthread_mutex_lock(&mutex);//加锁当有线程已经加速的时候会阻塞
		printf("hello world\n");
		sleep(30);
		//释放锁
		pthread_mutex_unlock(&mutex);
	}
}

int main()
{
	pthread_mutex_init(&mutex,NULL);
	pthread_t tid;
	pthread_create(&tid,NULL,thr,NULL);
	sleep(1)
	while(1){
		int ret=pthread_mutex_trylock(&mutex);
		if(ret>0)
		{
			printf("ret=%d,srrmsg:%s\n",ret,strerror(ret));
		}
		sleep(1);
	}
	return 0;
}

在这里插入图片描述
错误信息在errno-base.h中:
在这里插入图片描述
在这里插入图片描述

04 死锁

死锁:

  • 锁了又锁,自己加了一次锁成功,又加了一次。
  • 交叉锁

在这里插入图片描述
互斥量只是建议锁。

05 读写锁

读写锁的特点:读共享,写独占,写优先级高。
读写锁仍然是一把锁,有不同的状态:

  1. 为加锁
  2. 读锁
  3. 写锁

读写锁场景练习:

  1. 线程A加写锁成功,线程B请求读锁
    线程B阻塞

  2. 线程A持有读锁,线程B请求写锁
    线程B阻塞

  3. 线程A拥有读锁,线程B请求读锁
    B加锁成功

  4. 线程A持有读锁,然后线程B请求写锁,然后线程C请求读锁
    BC阻塞
    A释放后,B加锁
    B释放后,C加速

  5. 线程A持有写锁,然后线程B请求读锁,然后线程C请求写锁
    BC阻塞
    A释放,C加锁
    C释放,B加锁

读写锁的使用场景:适合读的线程多。

初始化

int pthread_rwlock_init(pthread_rwlock_t* restrict rwlock,const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;

销毁读写锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

加读锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

加写锁

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

释放锁

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

rwlock.c:

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

pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;
int beginnum=1000;

void *thr_write(void *arg)
{
	while(1)
	{
		pthread_rwlock_wrlock(&rwlock);
		printf("---%s---self---%lu---beginnum---%d\n",_FUNCTION_,pthread_self(),++beginnum);
		usleep(2000);//模拟占用时间
		pthread_rwlock_unlock(&rwlock);
		usleep(3000);
	}
	return NULL;
}

void *thr_read(void *arg)
{
	while(1)
	{
		pthread_rwlock_rdlock(&rwlock);
		printf("---%s---self---%lu---beginnum---%d\n",_FUNCTION_,pthread_self(),++beginnum);
		usleep(2000);//模拟占用时间
		pthread_rwlock_unlock(&rwlock);
		usleep(2000);
	}
	return NULL;
}

int main()
{
	int n=8,i=0;
	pthread_t tid[8];//5-read,3-write
	for(i=0;i<5;i++)
	{
		pthread_create(&tid[i],NULL,thr_read,NULL);
	}
	for(;i<8;i++)
	{
		pthread_create(&tid[i],NULL,thr_write,NULL);
	}
	for(i=0;i<8;i++)
	{
		pthread_join(tid[i],NULL);
	}
	return 0;
}

读共享,写独占。
在这里插入图片描述

06 条件变量介绍和生产者和消费者模型

条件变量不是锁,要和互斥量组合使用
在这里插入图片描述
超时等待

int pthread_cond_timedwait(pthread_cond_t *restict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
struct timespec{
time_t ty_sec; /second/秒
long ty_nsec;/nanoseconds/纳秒
}
ty_sec 绝对时间,填写的时候time(NULL)+600==>设置超时600s

条件变量阻塞等待

int pthread_cond_wait(pthread_cond_t *restict cond,pthread_mutex_t *restrict mutex);

  • 先释放锁mutex;
  • 阻塞在cond条件变量上

销毁一个条件变量

int pthread_cond_destroy(pthread_cond_t *restict cond);

初始化一个条件变量

int pthread_cond_init(pthread_cond_t restict cond,const pthread_condattr restric attr);
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

唤醒至少一个阻塞在条件变量cond上的线程

int pthread_cond_signal(pthread_cond_t * cond);

唤醒阻塞在条件变量cond上的全部线程

int pthread_cond_broadcast(pthread_cond_t * cond);

07 条件变量介绍和生产者和消费者实现

cond_product.c:

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

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PHTREAD_COND_INITIALIZER;

int beginnum=1000;

typedef struct _ProdInfo
{
	int num;
	struct _ProdInfo *next;
}ProdInfo;

ProdInfo *Head=NULL;

void *thr_producter(void *arg)
{
	//负责在链表添加数据
	while(1)
	{
		ProdInfo * prod=malloc(sizeof(ProdInfo));
		prod->num=beginnum++;
		printf("----%s------self=%lu----%d\n",_FUNCTION_,pthread_self(),prod->num);
		pthread_mutex_lock(&mutex);
		//add to list
		prod->next=Head;
		Head=prod;
		pthread_mutex_unlock(&mutex);
		//发起通知
		pthread_cond_signal(&cond);
		sleep(rand()%2);
	}
	return NULL;
}

void *thr_customer(void *arg)
{
	ProdInfo *prod=NULL;
	while(1)
	{
		//取链表的数据
		pthread_mutex_lock(&mutex);
		while(Head==NULL)
		{
			pthread_cond_wait(&cond,&mutex);//在此之前必须先加锁
		}
		prod=Head;
		Head=Head->next;
		printf("----%s------self=%lu----%d\n",_FUNCTION_,pthread_self(),prod->num);
		pthread_mutex_unlock(&mutex);
		sleep(rand()%4);
		free(prod);
	}
	return NULL;
}

int main()
{
	pthread_t tid[3];
	pthread_create(&tid[0],NULL,thr_producter,NULL);
	pthread_create(&tid[1],NULL,thr_customer,NULL);
	pthread_create(&tid[2],NULL,thr_customer,NULL);
	
	pthread_join(tid[0],NULL);
	pthread_join(tid[1],NULL);
	pthread_join(tid[2],NULL);
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
	return 0;
}

在这里插入图片描述

08 信号量的概念和函数

信号量是进化版的互斥量,允许多个线程访问共享资源。
在这里插入图片描述

int sem_init(sem_t *sem,int pshared,unsigned int value);

  • sem定义的信号量,传出

  • pshared
    0代表线程信号量
    非0代表进程信号量

  • value 定义信号量的个数

sem_destroy摧毁信号量

int sem_destroy(sem_t *sem);

申请信号量,申请成功value–;

int sem_wait(sem_t *sem)

  • 当信号量为0时,阻塞

释放信号量value++

int sem_post(sem_t *sem)

在这里插入图片描述

09 信号量实现生产者和消费者分析

在这里插入图片描述

10 信号量实现生产者和消费者实现

sem_product.c:

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

sem_t black,xfull;
#define _SEM_CNT_ 5

int queue[_SEM_CNT_];//模拟饼框
int beginnum=100;

void *thr_producter(void *arg)
{
	int i=0;
	while(1)
	{
		sem_wait(&blank);//申请资源 blank--
		printf("----%s-----self--%lu----num----%d\n",_FUNCTION_,pthread_self(),beginnum);
		queue[(i++)%_SEM_CNT_]=beginnum++;
		sem_post(&xfull);//xfull++;
		sleep(rand()%3);
	}
	return NULL;
}

void *thr_customer(void *arg)
{
	int i=0;
	int num=0;
	while(1)
	{
		sem_wait(&xfull);
		num=queue[(i++)%_SEM_CNT_];
		printf("----%s-----self--%lu----num----%d\n",_FUNCTION_,pthread_self(),num);
		sem_post(&blank);
		sleep(rand()%3);
	}
	return NULL;
}

int main()
{
	sem_init(&blank,0,_SEM_CNT);
	sem_init(&xfull,0,0);//消费者一开始的初始化默认没有产品
	
	pthread_t tid[2];
	
	pthread_create(&tid[0],NULL,thr_producter,NULL);
	pthread_create(&tid[1],NULL,thr_customer,NULL);
	
	pthread_join(tid[0],NULL);
	pthread_join(tid[1],NULL);

	sem_destroy(&blank);
	sem_destroy(&xfull);
	return 0;
}

在这里插入图片描述

11 文件锁单开进程

int fcntl(int fd,int cmd,long arg);
int fcnt1(int fd, int cmd, struct flock *lock)

函数传入值cmd
F_DUPFD:复制一个现存的描述符
F_GETFD:获得fd的close-on-exec(执行时关闭)文件描述符标志,若标志未设置,则文件经过exec()函数之后仍保持打开状态
F_SETFD:设置close-on-exec 标志,该标志由参数arg 的FD_CLOEXEC位决定
F_GETFL:得到open设置的标志
F_SETFL :改变open设置的标志
F_GETLK:根据lock参数值,决定是否可以上文件锁
F_SETLK:设置lock参数值的文件锁

在这里插入图片描述
flock.c:

#include<stdio.h>
#include<unistd.h>
#include<sys/type.h>
#include<sus/stat.h>
#include<fcnt1.h>

#define _FILE_NAME_ "/home/itheima/temp.lock"

int main()
{
	int fd=open(_FILE_NAME_,O_RDWR|O_CREAT,0666);
	if(fd<0)
	{
		perror("open err");
		return -1;
	}
	struct flock lk;
	lk.l_type=F_WRLCK;
	lk.l_whence=SEEK_SETl
	lk.l_start=0;
	lk.l_len=0;

	if(fcntl(fd,F_SETLK,&lk)<0)
	{
		perror("get lock err");
		exit(1);
	}
	//核心逻辑
	while(1)
	{
		printf("I am alive!\n");
		sleep(1);
	}
	return 0;
}

在这里插入图片描述

12 哲学家就餐模型分析

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值