生产者使用者同步仅使用互斥量和信号量(无pthread_cond)

时间:2019-06-25 16:37:34

标签: c linux pthreads

我目前正在学习如何使用pthread在Linux中使用互斥锁和信号量进行多线程处理,并且我一直在研究仅使用两个二进制信号量和一个互斥量来同步访问的mutlti-producer / one-Consumer问题的实现。到有限缓冲区,但程序无法按计划工作

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

#define QUEUESIZE 20
#define LOOP 15
#define MAX_THREADS 10

void *producer (void *args);
void *consumer (void *args);

int lambda;
int queuesi;
typedef struct {
        int buf[QUEUESIZE];
        long head, tail;
        int full, empty;
        pthread_mutex_t *mut; 
    sem_t *sem_notFull, *sem_notEmpty;
} queue;

queue *queueInit (void);
void queueDelete (queue *q);
void queueAdd (queue *q, int in);
void queueDel (queue *q, int *out);
int factorial( int i);           
int poissonfunction(int i);
int rando();

int main (int argc, char *argv[])
{
        int max_pro,max_con,j,i;
        queue *fifo;
        pthread_t prod[MAX_THREADS],cons[MAX_THREADS];

        max_con=1;
        max_pro=4;
        lambda=6;
        queuesi=4;  

        fifo = queueInit ();

        if (fifo ==  NULL) {
                fprintf (stderr, "main: Queue Init failed.\n");
                exit (1);
        }


        for(i=0; i<max_pro;i++)  
        pthread_create (&prod[i], NULL, producer,fifo);

        for(j=0;j<max_con;j++)
        pthread_create (&cons[j], NULL, consumer, fifo);

        for(i=0; i<max_pro;i++)
        pthread_join (prod[i], NULL);

        for(j=0;j<max_con;j++)
        pthread_join (cons[j], NULL);     

        queueDelete (fifo);

        return 0;
}

 void *producer (void *q)
{
        queue *fifo;
        int i,insert,sleep_time;     
        fifo = (queue *)q;

        for (i = 0; i < LOOP; i++) {
            //pthread_mutex_lock (fifo->mut);
            while (fifo->full)
               {
                    printf ("producer: queue FULL.\n");
                    sem_wait (fifo->sem_notFull);
                }

             pthread_mutex_lock (fifo->mut);
             insert=rando();     
             queueAdd (fifo, insert);  
             printf("producer item  number%d item produced  %d\n",i,insert);
             pthread_mutex_unlock (fifo->mut);

             sem_post (fifo->sem_notEmpty);
             sleep_time=poissonfunction(i);
             usleep (sleep_time);
        }

        return (NULL);
}                           

 void *consumer (void *q)
{
        queue *fifo;
        int i, d;

        fifo = (queue *)q;

        for (i = 0; i < LOOP; i++) 
        {
            //pthread_mutex_lock (fifo->mut);
            while (fifo->empty) 
            {      
               printf ("consumer: queue empty\n");
               sem_wait (fifo->sem_notEmpty);
            }
            pthread_mutex_lock (fifo->mut);
            queueDel (fifo, &d);
            pthread_mutex_unlock (fifo->mut);

            sem_post (fifo->sem_notFull);
            printf ("consumer: recieved %d.\n", d);
            usleep(2000);
        }                 

  return (NULL);
}

queue *queueInit (void)
{
        queue *q;

        q = (queue *)malloc (sizeof (queue));
        if (q == NULL) return (NULL);       
        q->empty = 1;
        q->full = 0;
        q->head = 0;
        q->tail = 0;
        q->mut = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
        pthread_mutex_init (q->mut, NULL);
    q->sem_notFull = (sem_t *) malloc (sizeof (sem_t));
        sem_init (q->sem_notFull, 0, 1);
        q->sem_notEmpty = (sem_t *) malloc (sizeof (sem_t));
        sem_init (q->sem_notEmpty,  0, 1); 

        return (q);
}

void queueDelete (queue *q)
{
        pthread_mutex_destroy (q->mut);
        free (q->mut);
        sem_destroy (q->sem_notFull);
        free (q->sem_notFull);   

        sem_destroy (q->sem_notEmpty);
        free (q->sem_notEmpty);

        free (q);
}

void queueAdd (queue *q, int in)
{
        q->buf[q->tail] = in;
        q->tail++;
        if (q->tail == queuesi)                     
        q->tail = 0;

        if (q->tail == q->head)
                q->full = 1;
        q->empty = 0;

        return;
}

void queueDel (queue *q, int *out)
{                                       
*out = q->buf[q->head];

        q->head++;
        if (q->head == queuesi)
                q->head = 0;
        if (q->head == q->tail)
                q->empty = 1;
        q->full = 0;

        return;                    
}

int factorial(int i){
 if(i==0)
  return i=1;
 else
  i=i*factorial(i-1);
return (i);
}               

int poissonfunction (int i){
    int time,c,t;
    double p;
    t = 1000;
    c = lambda*t;
    p = (pow(c,i)*exp(-c))/factorial(i);
    time =(int) p*t;
    return (time);
}

int rando(){
    int value;
    value=(int) random()/1000;
    return (value);
}

1 个答案:

答案 0 :(得分:1)

我建议修改您的功能,例如:

void *producer (void *q)
{   
    queue *fifo;
    int i,insert,sleep_time;
    fifo = (queue *)q;

    for (i = 0; i < LOOP; i++) {
        pthread_mutex_lock (fifo->mut); /* take lock */
        while (fifo->full) {  /* check for fullness */
            pthread_mutex_unlock (fifo->mut); /* if full wait for things to be removed */
            sem_wait (fifo->sem_notFull); /* never sleep with lock */
            pthread_mutex_lock (fifo->mut); /* take lock an recheck the condition to handle spurious wakeups */
        }
        /* here lock is held */
        insert = rando(); /* safely manipulate the queue */
        queueAdd (fifo, insert);

        pthread_mutex_unlock (fifo->mut); /* safely release the queue */
        sem_post (fifo->sem_notEmpty);  /* wake up potential waiters */
        sleep_time=poissonfunction(i);
        usleep (sleep_time);
    }

    return (NULL);
}

void *consumer (void *q)
{
    queue *fifo;
    int i, d;

    fifo = (queue *)q;

    for (i = 0; i < LOOP; i++) { /* careful you don't produce 'enough' */
        pthread_mutex_lock (fifo->mut); /* take lock before using fifo */
        while (fifo->empty) { /* check is something to read */
            pthread_mutex_unlock (fifo->mut); /* release lock before sleep */
            sem_wait (fifo->sem_notEmpty); /* wait a sometihing to be pused in queue */
            pthread_mutex_lock (fifo->mut); /* take back lock before checking for spurious wakeup */
        }
        /* from here lock is held, queue can be safely maniplated */
        queueDel (fifo, &d); 
        pthread_mutex_unlock (fifo->mut); /* finished -> release lock */
        sem_post (fifo->sem_notFull); /* wake up eventual waiters*/

        usleep(2000);
    }

    return (NULL);
}

您需要检查虚假唤醒,因为即使队列不为空/已满,信号量也会始终得到通知,因此被唤醒并不意味着队列处于正确状态,因此需要进行测试。

您可以使用conditional_variable来处理full和empty事件,这将删除此处的信号灯。