Multithreading 在SPSC框架条件_变量中。notify_one()的信号发送不一致

Multithreading 在SPSC框架条件_变量中。notify_one()的信号发送不一致,multithreading,mutex,producer-consumer,condition-variable,thread-synchronization,Multithreading,Mutex,Producer Consumer,Condition Variable,Thread Synchronization,生产者在每次推入队列后,通过conditionVar.notify_one()向消费者发出信号 然而,消费者在一些随机次数的推送(因此随后的notify_one()发生)后醒来,有时21次,有时2198次,等等。 在生成器中插入延迟(sleep_for()或yield())没有帮助 如何使此SPSC以锁定的方式运行 如何将此示例扩展到多个消费者(即SPMC) void singleProducerSingleConsumer(){ std::condition\u变量conditionVar;

生产者在每次推入队列后,通过conditionVar.notify_one()向消费者发出信号

然而,消费者在一些随机次数的推送(因此随后的notify_one()发生)后醒来,有时21次,有时2198次,等等。 在生成器中插入延迟(sleep_for()或yield())没有帮助

如何使此SPSC以锁定的方式运行

如何将此示例扩展到多个消费者(即SPMC)

void singleProducerSingleConsumer(){
std::condition\u变量conditionVar;
std::互斥mtx;
std::queue messageQueue;
bool-stopped=false;
const std::size\u t workSize=4096;
std::函数producerLambda=[&](int64_t id){
//准备一个随机数生成器并推送到队列
std::default_random_引擎randomNumberGen{};
标准:均匀分布均匀分布{};
对于(自动计数=0;计数<工作大小;计数++){
//在更改由互斥锁和条件变量“conditionVar”保护的状态之前始终锁定
std::lock_guard lockGuard{mtx};
//将随机数推送到队列上
push(uniformDistribution(randomNumberGen));
//通知消费者
conditionVar.notify_one();
//std::this_thread::yield();
/*std::this_thread::sleep_for(std::chrono::seconds(2));

std::cout如果我理解正确,您希望在生产者生成下一个数字之前使用生产者生成的每个数字。这基本上是顺序执行,而不是并发执行。您可以通过让生产者以正常功能将值传递给消费者来最有效地完成顺序执行结果将不是生产者-消费者模式的一个好例子

线程由操作系统调度,以与同时在计算机上执行的所有其他线程和进程竞争。生产者线程和使用者线程可能在同一cpu内核上运行,这意味着它们必须按照操作系统的调度轮流执行。因为使用者不能使用在生产者写入数据之前,生产者将在消费者有时间使用messageQueue中的值之前,在其第一个执行窗口中使用多个值填充messageQueue是有意义的。因此,在程序完成之前,messageQueue将分批填充和取消填充


您的解决方案应该扩展到处理多个使用者。

感谢您的输入。此代码将演变为ASIC模拟器;生产者将向不同的子系统(使用者线程)分派工作当你仍然持有互斥锁时,在你的
for
循环中没有延迟可以帮助你的消费者,他们正在耐心地等待相同的互斥锁。将你的
屈服
移动到
lockGuard
采集之前,你可能会看到“更公平”的行为(更“连续”和更少“批量”)。我会这样做。
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;
}