线程学习四(条件变量,信号量、读写锁)

条件变量(通知法)

查询法,需要反复等着一个事件的发生:不停查看状态。上文的任务池就是一直查看状态。下面介绍通知法,使用的是线程的条件变量,pthread_cond_t:
相关函数:
pthread_cond_init();
pthread_cond_destroy();
---------------------- destroy and initialize condition variables

    #include <pthread.h>
    int pthread_cond_destroy(pthread_cond_t *cond);
    int pthread_cond_init(pthread_cond_t *restrict cond,
  		     const pthread_condattr_t *restrict attr);//动态初始化
	    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//静态初始化,按照默认的初始化的属性

pthread_cond_broadcast();(发消息来打断一个wait)这里指的是pthread_cond_wait(下同)
pthread_cond_signal();
--------------------- broadcast or signal a condition

#include <pthread.h>
 int pthread_cond_broadcast(pthread_cond_t *cond);//把所有的等待都叫醒
 int pthread_cond_signal(pthread_cond_t *cond);//叫醒任意一个

pthread_cond_wait();
pthread_cond_timedwait();
----------------------wait on a condition

#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex,
  	     const struct timespec *restrict abstime);//超时设置,若没有相应的行为时,就结束。
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);//一直等

wait相当于解锁等待,wait一定和一个或是多个位置的signal或broadcast是互相对应的。
筛质数更改
上游和下游不停的抢num,看状态。
上游往num中放一个任务之后,发一个通知。下游的几个都在wait,叫醒任意一个来计算就可以。如果下游都在忙,signal或是broadcast没有打断任何一个wait再看后续操作.

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

#define LEFT  30000000
#define RIGHT 30000200
#define THRNUM 4
static void * thr_prime(void *p);
static int num=0;
static pthread_mutex_t mut_num=PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_num=PTHREAD_COND_INITIALIZER;

int main()
{
        int i,err;
        pthread_t tid[THRNUM];
        for(i=0;i<=THRNUM;i++)
        {
            
                err=pthread_create(tid+i,NULL,thr_prime,(void *)i);
                if(err)
                {
                        fprintf(stderr,"pthread_create():%s\n",strerror(err));
                        exit(1);
                }

        }


	for(i=LEFT;i<=RIGHT;i++)
	{
	pthread_mutex_lock(&mut_num);
	    while(num!=0)
		{
		pthread_cond_wait(&cond_num,&mut_num);
		
		}
	num=i;
	pthread_cond_signal(&cond_num);
	pthread_mutex_unlock(&mut_num);

	}

	pthread_mutex_lock(&mut_num);
	while(num!=0)
	{
		pthread_mutex_unlock(&mut_num);
		sched_yield();
		pthread_mutex_lock(&mut_num);
	}

	num=-1;
	pthread_mutex_unlock(&mut_num);


        for(i=0;i<=THRNUM;i++)
        {
                pthread_join(tid[i],NULL);
        }
	pthread_mutex_destroy(&mut_num);
	pthread_cond_destroy(&cond_num);
        exit(0);
}

static void * thr_prime(void *p)
{
        int i,j,mark;
	while(1)
	{
	pthread_mutex_lock(&mut_num);
	while(num==0)
	{
		pthread_cond_wait(&cond_num,&mut_num);
	}
	if(num==-1)
		{
			pthread_mutex_unlock(&mut_num);
			break;
		}
        i=num;
	num=0;
	pthread_cond_broadcast(&cond_num);
	pthread_mutex_unlock(&mut_num);
        mark=1;
        for(j=2;j<i/2;j++)
        {
                if(i%j==0)
                {
                mark=0;
                break;
                }
        }

        if(mark)
        printf("[%d]%d is a primer\n",(int)p,i);
	}
        pthread_exit(NULL);
}

abcd的改编:使用通知法:

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

#define THRNUM 4

static pthread_mutex_t mut=PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
static int num=0;
static int next(int n)
{
	if(n+1==THRNUM)
		return 0;
	return n+1;

}

static void *thr_func(void *p)
{
	int n=(int)p;
	int c='a'+n;

	while(1)
	{
	pthread_mutex_lock(&mut);
	while(num!=n)
	{
		pthread_cond_wait(&cond,&mut);
	}
	write(1,&c,1);
	num=next(num);
	pthread_cond_broadcast(&cond);
	pthread_mutex_unlock(&mut);
	}
	pthread_exit(NULL);
		
}

int main()
{
	int i,err;
	pthread_t tid[THRNUM];
	for(i=0;i<THRNUM;i++)
	{
		
		err=pthread_create(tid+i,NULL,thr_func,(void *)i);
		if(err)
		{
		fprintf(stderr,"pthread_create():%s\n",strerror(err));
		exit(1);
		}

	}
	
	alarm(5);

	for(i=0;i<THRNUM;i++)
	{
                pthread_join(tid[i],NULL);
        }


	pthread_mutex_destroy(&mut);
	pthread_cond_destroy(&cond);

	exit(0);

}

运行描述:
创建四个线程,这四个同时在运行,先来看bcd这三个线程的,假设b抢到这个锁,然后发现num不是自己,就在那解锁等待,如果cd抢到锁,也是这样,如果最后a来了,抢到锁了,发现是自己,就开始写a,把num值写成1,就相当于b的那个线程,然后发通知,就叫醒bcd的wait,然后这三个线程开始抢锁,加入若是d抢到的,那么发现不是自己就继续解锁等待,等到最后b抢到了,然后就写b,唤醒其他的两个wait,这两个再继续抢锁
抢到发现是自己就打印,不是自己就解锁等待,这样反复执行下去。

信号量

信号量:(共享),多个资源,用的时候就减减,还回去的时候资源就加加
用互斥量和条件变量完成信号量的使用。哲学家就餐问题。

以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用(百度描述)。

筛质数,以信号量的方法实现。
main.c文件内容:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "mysem.h"

#define LEFT  30000000
#define RIGHT 30000200
#define THRNUM (RIGHT-LEFT+1)
#define N 4


static void * thr_prime(void *p);

static mysem_t *sem;

int main()
{
        int i,err;
        pthread_t tid[THRNUM];
	
	sem=mysem_init(N);
	if(sem==NULL)
	{
		fprintf(stderr,"mysem_init() failed\n");
		exit(1);
	}
        for(i=LEFT;i<=RIGHT;i++)
        {	
		mysem_sub(sem,1);
                err=pthread_create(tid+(i-LEFT),NULL,thr_prime,(void *)i);
                if(err)
                {
                        fprintf(stderr,"pthread_create():%s\n",strerror(err));
                        exit(1);
                }

        }       
        for(i=LEFT;i<=RIGHT;i++)
                pthread_join(tid[i-LEFT],NULL);
	mysem_destroy(sem);
        exit(0);
}

static void * thr_prime(void *p)
{
        int i,j,mark;
        i=(int )p;
        mark=1;
        for(j=2;j<i/2;j++)
        {
                if(i%j==0)
                {
                mark=0;
                break;
                }
        }

        if(mark)
	        printf("%d is a primer\n",i);
	sleep(5);
	mysem_add(sem,1);
        pthread_exit(NULL);
}

mysem.c文件内容

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "mysem.h"


struct mysem_st

{
	int value;
	pthread_mutex_t mut;
	pthread_cond_t cond;
};


mysem_t *mysem_init(int initval)
{
	struct mysem_st *me;
	me=malloc(sizeof(*me));
	if(me==NULL)
	{
	return NULL;
	}
	me->value=initval;
	pthread_mutex_init(&me->mut,NULL);
	pthread_cond_init(&me->cond,NULL);

	return me;

}

int mysem_add(mysem_t *ptr,int n)
{
	struct mysem_st *me=ptr;
	pthread_mutex_lock(&me->mut);

	me->value+=n;
	pthread_cond_broadcast(&me->cond);
	pthread_mutex_unlock(&me->mut);
	return n;
}

int mysem_sub(mysem_t *ptr,int n)
{
	struct mysem_st *me=ptr;

	pthread_mutex_lock(&me->mut);
	while(me->value<n)
	{
		pthread_cond_wait(&me->cond,&me->mut);
	}
	me->value-=n;

	pthread_mutex_unlock(&me->mut);
	return n;

}
int mysem_destroy(mysem_t *ptr)
{
	struct mysem_st *me=ptr;
	pthread_mutex_destroy(&me->mut);
	pthread_cond_destroy(&me->cond);
	free(me);
	return 0;
}

mysem.h文件内容:

#ifndef MYSEM_H__
#define MYSEM_H__

typedef void mysem_t;
mysem_t *mysem_init(int initval);

int mysem_add(mysem_t *,int);

int mysem_sub(mysem_t *,int);
int mysem_destroy(mysem_t *);
#endif

makefile文件内容:
CFLAGS+=-pthread
LDFLAGS+=-pthread


all:mysem
mytbf:main.o mysem.o
	gcc $^ -o $@ $(CFLAGS) $(LDFLAGS)


clean:
	rm -rf *.o mysem

make编译以后,如果出现问题,那就选择一个一个来编译:
cc -pthread -c -o main.o main.c
gcc main.o mysem.o -o mysem -pthread -pthread

读写锁

读写锁:相当于互斥量和信号量的综合使用,包含两种锁,一种是读锁,一种是写锁。
读锁:相当于共享的内容,有点像信号量
写锁:像是互斥量,是一种互斥的内容
如果有一个文件,要么是读锁,要么是写锁,如果当前文件加的是读锁,如果再来一个读者,再加上一个读锁是可以实现的。如果当前文件本身是一个写锁,那么再加一个读锁或是写锁,是不可以实现的。因为如果在写的时候,别人在读会出现偏差,也不会是两个人同时在写。所以写锁相当于互斥锁,读锁相当于共享锁。整个过程的模拟是这样的:有一个读者来读文件,文件就加上读锁,又有一个读者到来,看到文件加的是读锁,那么它也可以加上读锁,进行读的操作。如果这两个读者在操作的同时,来了一个写者,发现这个文件有人在读,那么就等待,写锁就加不上。(但是如果源源不断有读者来读,写者没有机会加上写锁,那么写者会出现写者饿死现象)。那就需要以下的操作:当有一个写者到来了,企图给这个文件加上写锁时候,这个写锁是加不上的,文件真正加的是读锁,但只要有这个写者来了,写者在等待的时候,那么这个文件在外看来,就如同加了写锁一般,但是实际上加的是读锁,那么后来的那些读者,在看到这个文件的时候,就不会看到文件的真正的状态,不会看到这个读的状态,而以为文件打了写锁。所以后面的读者,暂时先等待,等到前面两个读者读完走了,写者真的把文件加上了写锁,后面的读者才能够把文件加上读锁,来继续读。这样就可以避免写者饿死的现象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值