条件变量解决多生产者多消费者问题

问题描述 投票:0回答:1

我目前正在学习操作系统,我遇到了“使用条件变量解决并发问题”。

任务是使用非常简约的 C 代码模拟一个具有大小为 10 的队列的多线程 Web 服务器,该队列存储客户端的请求。有 2 个生产者线程(客户端)和 2 个消费者线程(工作人员)。

现在我有几个相同的问题 -:

  1. 在以下两种方法中应该使用哪一种(考虑到我不必使用任何异步方法并且必须保持代码非常简单)来存储客户端请求 -:

选项 1:覆盖插槽

    Example:
    Queue size = 5
    Indexes: [0] [1] [2] [3] [4]

    Client 1 puts request at [0] Queue: [R1] [ ] [ ] [ ] [ ]
    Client 2 puts request at [1] Queue: [R1] [R2] [ ] [ ] [ ]
    Worker takes [0] Queue: [ ] [R2] [ ] [ ] [ ]
    Client 1 puts new request at [0] (reuses slot) Queue: [R3] [R2] [ ] [ ] [ ]

选项 2-: 附加到末尾

    Example:
    Client 1 puts request at [0]
    Queue: [R1] [ ] [ ] [ ] [ ] Write = 1
    Client 2 puts request at [1] Queue: [R1] [R2] [ ] [ ] [ ] Write = 2
    Worker takes [0] Read = 1
    Client 3 puts request at [2]
    Queue: [ ] [R2] [R3] [ ] [ ] Write = 3
  1. 考虑同一个任务,队列在什么情况下需要互斥体,什么情况下不需要?

  2. 以下代码中可以纠正哪些内容以使其正常工作?

上述场景的 C 代码 -:

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

#define MAX_REQUESTS 10

// Shared resource
int requestQueue[MAX_REQUESTS];
int rear = -1;
int front = 0;

pthread_mutex_t queueMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t queueNotEmpty = PTHREAD_COND_INITIALIZER;
pthread_cond_t queueNotFull = PTHREAD_COND_INITIALIZER;

// Producer (Client) function
void* client(void* arg) {

    int clientId = *((int*)arg);

    while (1) {
        // Simulating request generation
        sleep(rand() % 1 + 1);
        printf("Client %d online...\n", clientId);
        pthread_mutex_lock(&queueMutex);
        while (rear == MAX_REQUESTS-1) {
            // Queue is full, waiting for a signal that the queue is not full
            printf("Waiting for queue not full signal...\n");
            pthread_cond_wait(&queueNotFull, &queueMutex);
            printf("Client %d received signal, checking condition again...\n", clientId);
        }

        requestQueue[++rear] = clientId;
        printf("Client %d generated a request. Total requests: %d\n", clientId, rear+1);
        // Signaling worker threads that the queue is not empty
        pthread_cond_signal(&queueNotEmpty);
        pthread_mutex_unlock(&queueMutex);
    }
    return NULL;
}

// Consumer (Worker) function
void* worker(void* arg) {

    int workerId = *((int*)arg);

    while (1) {
        printf("Worker %d ready...\n", workerId);
        pthread_mutex_lock(&queueMutex);
        while (front > rear) {
            // Queue is empty, waiting for a signal that the queue is not empty
            printf("Waiting for queue not empty signal...\n");
            pthread_cond_wait(&queueNotEmpty, &queueMutex);
            printf("Worker %d received signal, checking condition again...\n", workerId);
        }

        int clientId = requestQueue[front++];
        printf("Worker %d processed a request from Client %d. Total requests: %d\n", workerId, clientId, rear+1);
        rear--;
        // Signaling clients that the queue is not full
        pthread_cond_signal(&queueNotFull);
        pthread_mutex_unlock(&queueMutex);
        // Simulating request processing
        sleep(rand() % 2 + 1);
    }
    return NULL;
}

int main() {

    srand(time(NULL));
    pthread_t clients[2];
    pthread_t workers[2];
    int clientIds[2] = {1, 2};
    int workerIds[2] = {1,2};

    for (int i = 0; i < 2; ++i) {
        pthread_create(&clients[i], NULL, client, &clientIds[i]);
        pthread_create(&workers[i], NULL, worker, &workerIds[i]);
    }

    for (int i = 0; i < 2; ++i) {
        pthread_join(clients[i], NULL);
        pthread_join(workers[i], NULL);
    }
    return 0;
}

代码输出-:

[Code output 1](https://i.stack.imgur.com/F7p74.jpg)
[Code output 2](https://i.stack.imgur.com/CGjig.jpg)
[Code output 3](https://i.stack.imgur.com/Tlqeq.jpg)
multithreading concurrency producer-consumer condition-variable critical-section
1个回答
0
投票

如果 Client1 与 Worker1 以及 Client2 与 Worker2 存在任何逻辑配对,则将 5 个队列元素分配给 Client1 <-> Worker1 对,并将剩余 5 个队列元素分配给 Client2 <-> Worker2 对。需要条件变量来控制 Client 何时写入内存位置以及 Worker 何时从内存位置读取。

如果客户端和工作人员没有逻辑配对,则队列应被视为循环队列,客户端写入“空”队列元素,而工作人员仅从“非空”队列元素读取。 “空”和“非空”的判断取决于程序中使用的条件变量。

非配对示例通常会实现由客户端共享的客户端写入索引变量以及由工作人员共享的工作人员读取索引变量。对于客户端共享的“完整”指示和工作人员共享的“空”指示也很有用。

客户端无法写入已满的队列。 Workers 无法从空队列中读取数据。

© www.soinside.com 2019 - 2024. All rights reserved.