利用信号量来调度共享资源 生产者-消费者问题

            信号量的的作用,互斥和同步。

        互斥就是确保对共享变量的互斥访问,基本思想就是:将每个共享变量与一个信号量s(初始化为1)联系起来,然后用P(s)和V(s)操作将相应的临界区包围起来。

        同步就是调度对共享资源的访问。信号量的同步:经典问题是生产者-消费者问题。

        生产者-消费者的问题描述如下:

  

         在本节中,我们将开发一个简单的包,叫做SBUF,用来构造生产者-消费者程序。在下一节中,我们会看到如何用它来构造一个基于预线程化的并发服务器。

         SBUF操作类型为sbuf_t的有限缓冲区,项目存放在一个动态分配的n項整数数组buf中。front 和 rear 索引值记录该数组的第一项和最后一项。三个信号量同步对缓冲区的访问mutex信号量提供互斥的缓冲区访问。slots和items信号量分别记录空槽位和可用项目的数量。

        提示:下面的代码是在linux下运行的,在window下运行不了,需要链接线程库 pthread,代码是从从深入理解计算机系统中摘录下来的。

        注:csapp.h 是包装函数,包含了各种头文件和相关函数的错误处理,等一下会说明。

sbuf_h文件:         

<span style="color:#ff0000;">#ifndef __SBUF_H__
#define __SBUF_H__</span>

#include "csapp.h"

/* $begin sbuft */
typedef struct {
    int *buf;          /* Buffer array */         
    int n;             /* Maximum number of slots */
    int front;         /* buf[(front+1)%n] is first item */
    int rear;          /* buf[rear%n] is last item */
    <span style="color:#ff0000;">sem_t mutex;       /* Protects accesses to buf */</span>
    <span style="color:#ff0000;">sem_t slots;       /* Counts available slots */</span>
    <span style="color:#ff0000;">sem_t items;       /* Counts available items */</span>
} sbuf_t;
/* $end sbuft */

void sbuf_init(sbuf_t *sp, int n);
void sbuf_deinit(sbuf_t *sp);
void sbuf_insert(sbuf_t *sp, int item);
int sbuf_remove(sbuf_t *sp);

<span style="color:#ff0000;">#endif</span> /* __SBUF_H__ */

注释: 

           1、学习头文件的定义方式,使用#ifndef

         2、sem_t 信号量定义在文件semaphore.h

         3、三个信号量同步对缓冲区的访问mutex信号量提供互斥的缓冲区访问。slots和items信号量分别记录空槽位和可用项目的数量。

         4、mutex:缓冲区的读取和写入不能同时进行,mutex保证对buffer的访问是互斥的

       现在给出 SBUF函数的实现,sbuf_init函数为缓冲区分配堆存储器,设置front 和 rear表示一个空的缓冲区,并为三个信号赋初始值。sbuf_deini是在应用程序使用完缓冲区时,释放缓冲区存储的。sbuf_insert函数等待一个可用的槽位,对互斥锁加锁,添加项目,对互斥锁解锁,然后宣布一个新项目可用。sbuf_remove在等待一个可用的缓冲区项目后,对互斥锁加锁,从缓冲区的前面取出该项目,对互斥锁解锁,然后发信号通知一个新的槽位可用。

/* $begin sbufc */
#include "csapp.h"
#include "sbuf.h"

/* Create an empty, bounded, shared FIFO buffer with n slots */
/* $begin sbuf_init */
void sbuf_init(sbuf_t *sp, int n)
{
    sp->buf = Calloc(n, sizeof(int)); 
    sp->n = n;                       /* Buffer holds max of n items */
    <span style="color:#ff6666;">sp->front = sp->rear = 0;        /* Empty buffer iff front == rear */</span>
<span style="color:#ff0000;">    Sem_init(&sp->mutex, 0, 1);      /* Binary semaphore for locking */
    Sem_init(&sp->slots, 0, n);      /* Initially, buf has n empty slots */
    Sem_init(&sp->items, 0, 0);      /* Initially, buf has zero data items */</span>
}
/* $end sbuf_init */

/* Clean up buffer sp */
/* $begin sbuf_deinit */
void sbuf_deinit(sbuf_t *sp)
{
    Free(sp->buf);
}
/* $end sbuf_deinit */

/* Insert item onto the rear of shared buffer sp */
/* $begin sbuf_insert */
void sbuf_insert(sbuf_t *sp, int item)
{
    <span style="color:#ff0000;">P(&sp->slots);   </span>                       /* Wait for available slot */
    <span style="color:#3333ff;">P(&sp->mutex);</span>                          /* Lock the buffer */
    sp->buf[(++sp->rear)%(sp->n)] = item;   /* Insert the item */
   <span style="color:#3366ff;"> V(&sp->mutex);</span>                          /* Unlock the buffer */
    V(&sp->items);                          /* Announce available item */
}
/* $end sbuf_insert */

/* Remove and return the first item from buffer sp */
/* $begin sbuf_remove */
int sbuf_remove(sbuf_t *sp)
{
    int item;
    P(&sp->items);                          /* Wait for available item */
    P(&sp->mutex);                          /* Lock the buffer */
    item = sp->buf[(++sp->front)%(sp->n)];  /* Remove the item */
    V(&sp->mutex);                          /* Unlock the buffer */
    <span style="color:#ff0000;">V(&sp->slots);</span>                          /* Announce available slot */
    return item;
}
/* $end sbuf_remove */
/* $end sbufc */

注释:

1、  Sem_init(&sp->mutex, 0, 1); -互斥锁初始化为1 

       Sem_init(&sp->slots, 0, n);   -初始化可用锁为n

       Sem_init(&sp->items, 0, 0); -初始化可用项目为0

2、互斥问题,P,V操作会在同一个进程(函数)中出现(代码中,标蓝的部分)

      同步问题,P,V操作会在不同的进程(函数)中出现(代码中, 标红的部分)

3、P(&sp->slots);P(&sp->mutex);这两个P操作不能对调,因为,若一个缓冲区已满,P(&sp->mutex),生产者占用了缓冲区,之后执行P(&sp->slots),因为缓冲区已满,生产者挂起,因为生产者占用了缓冲区,所以,消费者就无法进入缓冲区,导致死锁。

4、V(&sp->mutex);V(&sp->items);是可以对调的,逻辑上不会出错,但颠倒之后临界区会扩大,不合适。

5、P操作可以是调用者进程挂起,但V操作不会使调用者进程挂起


接下来:介绍上面使用过的几个函数,都是包装完了,包含了错误处理,详情见博客:http://blog.csdn.net/lujiandong1/article/details/45486063

strerror(errno):获取errno对应的错误

errno 是全局错误整数变量,由系统定义

下面的几个函数都是在 csapp.c中定义的

/************************** 
 * Error-handling functions
 **************************/
/* $begin errorfuns */
/* $begin unixerror */
  void unix_error(char *msg) /* unix-style error */
{
    fprintf(stderr, "%s: %s\n", msg, strerror(errno));
    exit(0);
}
/* $end unixerror */

void *Calloc(size_t nmemb, size_t size) 
{
    void *p;

    if ((p = calloc(nmemb, size)) == NULL)
	unix_error("Calloc error");
    return p;
}

void Free(void *ptr) 
{
    free(ptr);
}

void Sem_init(sem_t *sem, int pshared, unsigned int value) 
{
    if (sem_init(sem, pshared, value) < 0)
	unix_error("Sem_init error");
}

void P(sem_t *sem) 
{
    if (sem_wait(sem) < 0)
	unix_error("P error");
}

void V(sem_t *sem) 
{
    if (sem_post(sem) < 0)
	unix_error("V error");
}


总结:1、使用错误包装处理函数

           2、SBUF我们可以很容易改成类的形式,可以把缓冲区数据类型拓展成其他数据类型。


     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值