C 中的一个使用者多个生产者在完全缓冲区后恢复时阻止赛车



我制作了一个循环缓冲区,多个客户端将不同长度的消息写入缓冲区。服务器将它们读出。它基于消费者/生产者问题的代码。问题是,当缓冲区已满并且服务器从缓冲区中删除所有数据时,客户端会收到恢复写入操作的信号,但另一个客户端(在另一个线程中)开始在缓冲区中写入消息。我希望在缓冲区已满之前已经在写入的客户端恢复其操作,以便消息不会乱序到达。

这是我的代码(我删除了很多测试代码)

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define BUFFER_SIZE 8
#define NUM_THREADS 4
struct cBuf{
    char    *buf; 
    int     size;   
    int     start;
    int     end;   
    pthread_mutex_t mutex;
    pthread_cond_t  buffer_full;
    pthread_cond_t  buffer_empty;
};
struct cBuf cb;

void buf_Init(struct cBuf *cb, int size) {
    int i;
    cb->size  = size + 1; 
    cb->start = 0;
    cb->end   = 0; 
    cb->buf = (char *)calloc(cb->size, sizeof(char)); 
    for (i=0;i<size;i++) cb->buf[i]='_';
}
void buf_Free(struct cBuf *cb) {
    free(cb->buf);
}
int buf_IsFull(struct cBuf *cb) {
    return (cb->end + 1) % cb->size == cb->start; 
}
int buf_IsEmpty(struct cBuf *cb) {
    return cb->end == cb->start; 
}
int buf_Insert(struct cBuf *cb, char *elem) {
    int i,j;
    pthread_mutex_lock(&(cb->mutex));
    for (i=0; i < strlen(elem); ++ i){
        if (buf_IsFull(cb)==1) printf("nProducer (buf_Insert) is waiting because of full buffer");
        while(buf_IsFull(cb)){  
            pthread_cond_signal(&(cb->buffer_full));            
            pthread_cond_wait(&(cb->buffer_empty),&(cb->mutex));
        } 
        cb->buf[cb->end] = elem[i]; 
        cb->end = (cb->end + 1) % cb->size;     
        printf("%c [INPUT]",elem[i]);
    }
    pthread_cond_signal(&(cb->buffer_full));
    pthread_mutex_unlock(&(cb->mutex));     
    return 0;       
}
int buf_Read(struct cBuf *cb, char *out) {
    int i,j;
    pthread_mutex_lock(&(cb->mutex));
    if (buf_IsEmpty(cb))printf("nConsumer (buf_Read) is waiting because of empty buffern");
    while(buf_IsEmpty(cb)){
        pthread_cond_wait(&(cb->buffer_full),&(cb->mutex));
    }
    for (i=0;i<BUFFER_SIZE-1;i++){
    printf("n");
        if (cb->start == cb->end) break;        
        out[i] = cb->buf[cb->start];
        cb->buf[cb->start] = '_';
        cb->start = (cb->start + 1) % cb->size; 
        printf("%c [OUTPUT]",out[i]);
    }
    pthread_cond_signal(&(cb->buffer_empty));
    pthread_mutex_unlock(&(cb->mutex)); 
    return 0;
}
void * client(void *cb){
    pthread_detach(pthread_self());
    struct cBuf *myData;
    myData = (struct cBuf*) cb;
    char input[]="Hello World!";
    if (buf_Insert(myData, input)){
        //succes on return 0
        printf("n");
    } 
    return 0;
}
int main(void) {
    char out[60];
    pthread_t thread;
    int i;
    /* Initialise conditioners*/
    pthread_cond_init(&(cb.buffer_full),NULL);
    pthread_cond_init(&(cb.buffer_empty),NULL);
    buf_Init(&cb, BUFFER_SIZE);
    for (i = 0; i<NUM_THREADS; i++){
            if(pthread_create (&thread,NULL, client, (void *) &cb) !=0){
        } else {
        }
    }
    while (1){
        if (buf_Read(&cb,out)){
        } 
    }
    //empty the buffer; free the allocated memory
    buf_Free(&cb);
    return 0;
}

我已经在生产者/消费者的评论中解释过,当缓冲区小于生产者的输入时,似乎陷入僵局,但这些都是注释,所以这里是答案:

队列中永远不应该有部分消息。确保你永远不要写一个。

您可以在开始写入消息之前检查是否有足够的空间,如果没有,请立即等待buffer_empty,或者您可以更改队列以将共享指针发送到分配的数据(将所有权传递给消费者或引用计数)或其他东西,因此每条消息仅占用队列中的一个插槽并为其余部分分配内存。最好的将取决于您消息的确切性质。只要没有部分消息,任何事情都可以。

虽然可以记录哪个特定的作者需要完成一条消息并唤醒它,但它会非常复杂。同步本身很难,不要通过对它提出额外的要求来使其更难。

事实上,除非这是家庭作业(从某种意义上说,你这样做是为了学习同步的工作原理),否则只需寻找现成的消息队列。数据报模式下的 SysV-IPC 套接字或 unix 域套接字是我想到的两个选项,或者寻找一些可以这样做的库。

最新更新