【发布时间】:2020-06-12 17:11:18
【问题描述】:
我正在尝试在 C++ 中实现生产者 消费者模式。 当我读到这种模式时,他们似乎总是提到必须避免的潜在死锁。 但是,我在下面没有使用任何互斥体就实现了这一点。 我的代码有什么问题?
#include <vector>
#include <iostream>
#include <chrono>
#include <thread>
#include <atomic>
class CircularBuffer
{
public:
CircularBuffer();
int* getWritePos();
void finishedWriting();
int* getReadPos();
void finishedReading();
private:
void waitForReaderToCatchUp();
void waitForWriterToCatchUp();
const int size = 5;
std::vector<int> data;
// Changed from int since these variables are shared between the two threads and assignment is not necessarily atomic:
std::atomic<int> writePos = 0;
std::atomic<int> readPos = 0;
};
CircularBuffer::CircularBuffer() {
data.resize(size);
}
void
CircularBuffer::waitForReaderToCatchUp() {
int unread = writePos - readPos;
while (unread >= size) {
std::this_thread::sleep_for(std::chrono::nanoseconds(10));
unread = writePos - readPos;
}
}
int*
CircularBuffer::getWritePos() {
waitForReaderToCatchUp();
int pos = writePos % size;
return &data[pos];
}
void
CircularBuffer::finishedWriting() {
writePos++;
}
void
CircularBuffer::waitForWriterToCatchUp() {
int unread = writePos - readPos;
while (unread < 1) {
std::this_thread::sleep_for(std::chrono::nanoseconds(10));
unread = writePos - readPos;
}
}
int*
CircularBuffer::getReadPos() {
waitForWriterToCatchUp();
int pos = readPos % size;
return &data[pos];
}
void
CircularBuffer::finishedReading() {
readPos++;
}
const int produceMinTime = 100;
void produce(CircularBuffer *cb) {
for (int i = 0; i < 15; i++) {
int r = rand() % 1000;
std::this_thread::sleep_for(std::chrono::milliseconds(produceMinTime + r));
int *p = cb->getWritePos();
memcpy(p, &i, 4);
cb->finishedWriting();
}
}
void consume(CircularBuffer *cb) {
for (int i = 0; i < 15; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int *p = cb->getReadPos();
int j = *p;
std::cout << "Value: " << j << std::endl;
cb->finishedReading();
}
}
int main()
{
CircularBuffer cb;
std::thread t1(produce, &cb);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
std::thread t2(consume, &cb);
t1.join();
t2.join();
int k;
std::cin >> k;
}
【问题讨论】:
-
我不明白你的问题是什么?如果你没有像互斥锁这样的东西,那么你就不会出现死锁。
-
不确定此代码中的死锁 - 你必须询问提出它的人 - 但任何时候你看到
this_thread::sleep_for()问题都应该问 -
您不会遇到死锁,但您会遇到更糟糕的情况:由于数据竞争导致的未定义行为。您需要同步原语来阻止它们。
-
代码不起作用,问题是它没有经过足够好的测试。数据竞争错误每月仅发生一次,一个月或一个月。另一个值得注意的缺陷是,在现代机器上,一毫秒是永恒的。不用自己写,google“c++线程安全循环缓冲区”找代码。
-
@HansPassant:谢谢。我把它改成了 10 纳秒。
标签: c++ multithreading