无法在 C++ 中使用列表创建生产者-消费者实例
Unable to make an Producer-Consumer instance with list in c++
伙计们。我正在学习生产者-消费者问题。我的教授给了我们一个示例代码,使用 classical int array
在两个线程之间共享资源。它按预期工作,但是,我想使用 C++ 中的 std:list
class 来尝试它,但它没有按预期工作。消费者似乎不尊重 sem_wait(&full);
所以它在共享列表中没有任何内容时尝试多次消费。
带有数组缓冲区的原始代码
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define N 2
#define TRUE 1
int buffer[N], in = 0, out = 0;
sem_t empty, full, mutexC, mutexP;
void *producer(void *arg) {
while(TRUE) {
sleep(rand()%5);
sem_wait(&empty);
sem_wait(&mutexP);
buffer[in] = rand() % 100;
printf("Producing buffer[%d] = %d\n", in, buffer[in]);
in= (in+1) % N;
sem_post(&mutexP);
sem_post(&full);
}
}
void *consumer(void *arg) {
while(TRUE) {
sleep(rand()%5);
sem_wait(&full);
sem_wait(&mutexC);
printf("Consuming buffer[%d] = %d\n", out, buffer[out]);
out = (out+1) % N;
sem_post(&mutexC);
sem_post(&empty);
}
}
int main(int argc, char *argv[ ]) {
pthread_t cons, prod;
sem_init(&mutexC, 0, 1);
sem_init(&mutexP, 0, 1);
sem_init(&empty, 0, N);
sem_init(&full, 0, 0);
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_exit(0);
}
我对列表的实现:
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <list>
#define TRUE 1
#define N 2
using namespace std;
list<int> sharedList;
sem_t empty, full, mutexC, mutexP;
void *producer(void *arg) {
while(TRUE) {
sleep(rand()%5);
sem_wait(&empty);
sem_wait(&mutexP);
int prod = rand() % 100;
cout << "producing: " << prod << endl;
sharedList.push_back(prod);
sem_post(&mutexP);
sem_post(&full);
}
}
void *consumer(void *arg) {
while(TRUE) {
sleep(rand()%5);
sem_wait(&full);
sem_wait(&mutexC);
if (!sharedList.empty()) {
cout << "consuming: ";
cout << sharedList.front() << endl;
sharedList.pop_front();
} else {
cout << "not possible to consume" << endl;
}
sem_post(&mutexC);
sem_post(&empty);
}
}
int main(int argc, char *argv[ ]) {
pthread_t cons, prod;
sem_init(&mutexC, 0, 1);
sem_init(&mutexP, 0, 1);
sem_init(&empty, 0, N);
sem_init(&full, 0, 0);
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_exit(0);
}
我的实施中的意外日志:
producing: 73
consuming: 73
not possible to consume
producing: 44
consuming: 44
producing: 9
producing: 65
consuming: 9
producing: 87
consuming: 65
consuming: producing: 29
87
not possible to consume
consuming: 29
producing: 9
producing: 60
consuming: 9
producing: 78
有人可以向我解释发生了什么吗?
提前致谢!
首先,请注意在两个程序中:
- 只有一个生产者争用信号量
mutexP
,该信号量没有任何用处。
- 同样,只有一个消费者曾经争用信号量
mutexC
,这也没有任何用处。
现在,考虑在您的第一个程序中使用值 2 而不是值 1 初始化信号量 empty
的目的是什么:我想您会同意这样做允许生产者 运行与消费者并驾齐驱,领先一个位置。这没问题,因为访问不同的数组元素不会冲突。
但是当您的存储是 std::list
而不是数组时,not 如果其中一个线程修改了它,则两个线程可以同时操作它。有几种互补的方式来看待它,其中:
a std::list
是单个对象,必须同步访问它,而数组只是其元素的集合,就同步而言不是其自身的独特对象要求有关。
a std::list
有成员——例如维护当前列表大小——所有操作都使用这些成员,必须同步访问这些成员。
无论您选择查看它,都必须确保如果其中一个线程修改它,std::list
不能被两个线程同时访问。
跟进
尽管第二个代码确实有错误,但事实证明 macOS 确实 忽略了 sem_wait()
。我试图用 0 初始化所有信号量,期望程序进入死锁,但实际上并没有。所以,如果macOS上运行,请参考this answer
rand() %6可以生成一些有趣的序列;所以虚假唤醒不可能消耗是可以预料的。
除此之外,唯一令人困惑的一点是:
consuming: producing: 29
87
not possible to consume
这不是那么令人困惑; io 语句交错。如果他们不这样做,您会看到:
consuming: 87
producing: 29
not possible to consume
其中最后一条语句是在生产者创建 29.
之前检查条件的结果
伙计们。我正在学习生产者-消费者问题。我的教授给了我们一个示例代码,使用 classical int array
在两个线程之间共享资源。它按预期工作,但是,我想使用 C++ 中的 std:list
class 来尝试它,但它没有按预期工作。消费者似乎不尊重 sem_wait(&full);
所以它在共享列表中没有任何内容时尝试多次消费。
带有数组缓冲区的原始代码
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define N 2
#define TRUE 1
int buffer[N], in = 0, out = 0;
sem_t empty, full, mutexC, mutexP;
void *producer(void *arg) {
while(TRUE) {
sleep(rand()%5);
sem_wait(&empty);
sem_wait(&mutexP);
buffer[in] = rand() % 100;
printf("Producing buffer[%d] = %d\n", in, buffer[in]);
in= (in+1) % N;
sem_post(&mutexP);
sem_post(&full);
}
}
void *consumer(void *arg) {
while(TRUE) {
sleep(rand()%5);
sem_wait(&full);
sem_wait(&mutexC);
printf("Consuming buffer[%d] = %d\n", out, buffer[out]);
out = (out+1) % N;
sem_post(&mutexC);
sem_post(&empty);
}
}
int main(int argc, char *argv[ ]) {
pthread_t cons, prod;
sem_init(&mutexC, 0, 1);
sem_init(&mutexP, 0, 1);
sem_init(&empty, 0, N);
sem_init(&full, 0, 0);
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_exit(0);
}
我对列表的实现:
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <list>
#define TRUE 1
#define N 2
using namespace std;
list<int> sharedList;
sem_t empty, full, mutexC, mutexP;
void *producer(void *arg) {
while(TRUE) {
sleep(rand()%5);
sem_wait(&empty);
sem_wait(&mutexP);
int prod = rand() % 100;
cout << "producing: " << prod << endl;
sharedList.push_back(prod);
sem_post(&mutexP);
sem_post(&full);
}
}
void *consumer(void *arg) {
while(TRUE) {
sleep(rand()%5);
sem_wait(&full);
sem_wait(&mutexC);
if (!sharedList.empty()) {
cout << "consuming: ";
cout << sharedList.front() << endl;
sharedList.pop_front();
} else {
cout << "not possible to consume" << endl;
}
sem_post(&mutexC);
sem_post(&empty);
}
}
int main(int argc, char *argv[ ]) {
pthread_t cons, prod;
sem_init(&mutexC, 0, 1);
sem_init(&mutexP, 0, 1);
sem_init(&empty, 0, N);
sem_init(&full, 0, 0);
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_exit(0);
}
我的实施中的意外日志:
producing: 73
consuming: 73
not possible to consume
producing: 44
consuming: 44
producing: 9
producing: 65
consuming: 9
producing: 87
consuming: 65
consuming: producing: 29
87
not possible to consume
consuming: 29
producing: 9
producing: 60
consuming: 9
producing: 78
有人可以向我解释发生了什么吗? 提前致谢!
首先,请注意在两个程序中:
- 只有一个生产者争用信号量
mutexP
,该信号量没有任何用处。 - 同样,只有一个消费者曾经争用信号量
mutexC
,这也没有任何用处。
现在,考虑在您的第一个程序中使用值 2 而不是值 1 初始化信号量 empty
的目的是什么:我想您会同意这样做允许生产者 运行与消费者并驾齐驱,领先一个位置。这没问题,因为访问不同的数组元素不会冲突。
但是当您的存储是 std::list
而不是数组时,not 如果其中一个线程修改了它,则两个线程可以同时操作它。有几种互补的方式来看待它,其中:
a
std::list
是单个对象,必须同步访问它,而数组只是其元素的集合,就同步而言不是其自身的独特对象要求有关。a
std::list
有成员——例如维护当前列表大小——所有操作都使用这些成员,必须同步访问这些成员。
无论您选择查看它,都必须确保如果其中一个线程修改它,std::list
不能被两个线程同时访问。
跟进
尽管第二个代码确实有错误,但事实证明 macOS 确实 忽略了 sem_wait()
。我试图用 0 初始化所有信号量,期望程序进入死锁,但实际上并没有。所以,如果macOS上运行,请参考this answer
rand() %6可以生成一些有趣的序列;所以虚假唤醒不可能消耗是可以预料的。
除此之外,唯一令人困惑的一点是:
consuming: producing: 29
87
not possible to consume
这不是那么令人困惑; io 语句交错。如果他们不这样做,您会看到:
consuming: 87
producing: 29
not possible to consume
其中最后一条语句是在生产者创建 29.
之前检查条件的结果