linux 多线程同步 2019.1.9(互斥量的使用,死锁,读写锁,条件变量,信号量,文件锁)

学习目标

 

 

互斥量的使用

 

 

lock和unlock的使用例子

 

通过互斥量,两个线程交替打印

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

//常量初始化锁——mutex(这样就不用init函数了),将其定义为全局变量
pthread_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;
}

 

 

trylock

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

pthread_mutex_t mutex;


void *thr(void *arg)
{
    while(1){
        pthread_mutex_lock(&mutex);
        printf("hello world\n");
        sleep(30);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}


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;
}


返回值的错误码是16,我们来看一下16代表什么意思。

 

错误码定义的地方

 

 

 

死锁

产生条件

  • 锁了又锁,自己加了锁,自己又加了一把锁。一般都是有分支或者写代码的时候忘了会出现这个问题
  • 交叉锁(如下图所示)——解决方案:1、每个线程申请锁的顺序要一致;2、如果申请到一把锁,另一个申请不到,则释放已有资源

互斥量只是建议锁

 

 

 

读写锁

读共享,写互斥,写的优先级高。适合读的线程多的场景

读写锁仍然是一把锁,有不同的状态:

  • 未加锁
  • 读锁
  • 写锁

 

读写锁练习场景

 

 

初始化

 

一种初始化的方式

 

 

另一种初始化的方式

 

 

销毁读写锁

 

 

读写锁的例子

#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(4000);
    }
    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(){
	//创建5个读锁和3个写锁
    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;
}

 

 

 

 

条件变量

引入原因:mutex会产生如下问题

多个线程抢到锁之后,发现并没有资源,因此释放锁,然后继续多线程抢锁,然后释放锁,这户造成资源的浪费。

三个吃货抢锁,抢到的人打开饼筐发现没有饼,于是释放锁,然后三个吃货继续抢锁。

 

条件变量可以引起阻塞,并非锁,要和互斥变量组合使用

 

 

超时等待

 

传入参数中的timespec结构体:

 

 

条件变量阻塞等待

 

使用条件变量解决问题的例子

两个线程,一个线程想把所有的内容都写成0,另一个线程想把所有的内容都写成1

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>

pthread_mutex_t mutex ;

char buf[20];

void *thr1(void *arg){

    int i = 0 ;
    //加锁
    pthread_mutex_lock(&mutex);
    for(;i< 20; i ++){
        usleep(rand()%3);
        buf[i] = '0';
    }
    //解锁
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void *thr2(void *arg) {
    int i = 0 ;
    //加锁
    pthread_mutex_lock(&mutex);
	for (; i < 20; i++) {
		usleep(rand() % 3);
		buf[i] = '1';
	}
    //解锁
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main(){
    //两个线程,一个线程想把所有的内容都写成0,另一个线程想把所有的内容都写成1
    memset(buf,0x00,sizeof(buf));
    //初始化条件变量
    pthread_mutex_init(&mutex, NULL);

    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);
    printf("buf is %s\n",buf);
    //释放条件变量
    pthread_mutex_destory(&mutex);
    return 0;
}

 

 

 

条件变量解决生产着消费者模型

 

一个生产者一个消费者时

链表存储数据

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

int beginnum = 1000;

//初始化mutex
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
//初始化条件变量
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

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

ProdInfo *HEAD = NULL;

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

void *thr_customer(void *arg){
	ProdInfo *prod=NULL;
	while(1){
		//取链表的数据
		pthread_mutex_lock(&mutex);
		//判断有没有数据
		if(HEAD==NULL){
			//发送消息等待
			pthread_cond_wait(&cond, &mutex);
		}
		//此时链表非空
		prod=HEAD;
		HEAD=HEAD->next;
		printf("--------%s--------seld=%lu------------%d\n",__FUNCTION__,pthread_self(), prod->num);
		//解锁
		pthread_mutex_unlock(&mutex);
		sleep(rand()%4);
		free(prod);
	}
	return NULL;
}



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

 

 

一个生产者多个消费者时

把thr_customer中的

if(HEAD==NULL)

改成

while(HEAD==NULL)

即可。

 

 

 

信号量

加强版的互斥锁。适于多个资源多个线程访问的情况。

 

 

初始化

  • sem 定义的信号量,传出
  • pshared 非0则代表进程信号量, 0代表线程信号量
  • value 定义信号量的并发数(钥匙的个数)

 

摧毁信号量

 

申请信号量,申请成功,则value--

 

释放信号量 value++

 

 

信号量实现生产者消费者模型

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

//blank只有多少可以放生产者生产东西的地方,xfull是消费者可以消费的东西的数量
sem_t blank,xfull;
#define _SEM_CNT_  5
// 模拟饼筐
int queue[_SEM_CNT_];
int beginnum = 100;

void *thr_producter(void *arg) {
    int i = 0;
    while(1){
        //申请资源 blank--
        //看看能不能生产,还有没有空间
        sem_wait(&blank);
        //打印函数名、线程名、数量
        printf("-----%s-----self--%lu----num----%d\n",__FUNCTION__,pthread_self(),beginnum);
        //生产数据
        queue[(i++)%_SEM_CNT_] = beginnum++;
        //xfull ++
        sem_post(&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(){
    //线程,所有第二个参数是0, 如果是进程,则第二个参数是非0
    sem_init(&blank,0,_SEM_CNT_);
    //消费者一开始的初始化默认没有产品
    sem_init(&xfull,0,0);

    pthread_t tid[2];

    //线程没有设置属性,所有第二个参数为NULL
    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;
}

 

 

 

文件锁

适合环境——当前系统中该进程只能起一个。

实现原理——当一个进程打开了这个文件,另一个进程发现文件被打开了,就无法再打开这个文件了

文件锁——读共享,写独占

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.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_SET ;
    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;
}

 

 

 

 

哲学家用餐问题

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值