The producer, after each push into the queue, signals the consumer via conditionVar.notify_one().
However, the consumer wakes up after some random number of pushes (hence the subsequent notify_one()s) take place, sometimes 21 sometimes 2198, etc. Inserting a delay(sleep_for() or yield()) in the producer does not help.
How can I make this SPSC operate in lock-step?
How can I extend this example into multiple consumers(i.e. SPMC)?
void singleProducerSingleConsumer() {
std::condition_variable conditionVar;
std::mutex mtx;
std::queue<int64_t> messageQueue;
bool stopped = false;
const std::size_t workSize = 4096;
std::function<void(int64_t)> producerLambda = [&](int64_t id) {
// Prepare a random number generator and push to the queue
std::default_random_engine randomNumberGen{};
std::uniform_int_distribution<int64_t> uniformDistribution{};
for (auto count = 0; count < workSize; count++){
//Always lock before changing state guarded by a mutex and condition_variable "conditionVar"
std::lock_guard<std::mutex> lockGuard{ mtx };
//Push a random number onto the queue
messageQueue.push(uniformDistribution(randomNumberGen));
//Notify the consumer
conditionVar.notify_one();
//std::this_thread::yield();
/*std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Producer woke " << std::endl;*/
}
//Production finished
//Acquire the lock, set the stopped flag, inform the consumer
std::lock_guard<std::mutex> lockGuard {mtx };
std::cout << "Producer is done!" << std::endl;
stopped = true;
conditionVar.notify_one();
};
std::function<void(int64_t)> consumerLambda = [&](int64_t id) {
do {
std::unique_lock<std::mutex> uniqueLock{ mtx };
//Acquire the lock only if stopped or the queue isn't empty
conditionVar.wait(uniqueLock, [&]() {return stopped || !messageQueue.empty(); });
//This thread owns the mutex here; pop the queue until it is empty
std::cout << "Consumer received " << messageQueue.size() << " items" << std::endl;
while (!messageQueue.empty()) {
const auto val = messageQueue.front(); messageQueue.pop();
std::cout << "Consumer obtained: " << val << std::endl;
}
uniqueLock.unlock();
if (stopped) {
//Producer has signaled a stop
std::cout << "Consumer is done!" << std::endl;
break;
}
} while (true);
};
std::thread consumer{ consumerLambda, 1 };
std::thread producer{ producerLambda, 2 };
consumer.join();
producer.join();
std::cout << "singleProducerSingleConsumer() finished" << std::endl;
}