消费者没有看到变量变化的状态

时间:2017-11-22 10:19:53

标签: multithreading c++11

在完整代码(见下文)中,在某些时候(在N次迭代之后)消费者(函数executionServiceHandler while(inService_)被输入,尽管inService_已被更改(在同一个线程中)[左右似乎是从日志输出]。

我尽可能地试图保护std :: cout,虽然我不确定这是否必要。

你可能会批评代码吗?我知道严格来说,我不需要使用StopServiceCmd让线程完成,但在我看来,我不明白它为什么不能工作。

另外,我本来可以使用std :: function,但是这个例子是从原版中简化的。

我可以补充一点,我在GCC上测试了这个(最新),原来的例子在VCC(2017)上失败了

代码如下(编辑 - 现在使用std :: function:

#include <thread>
#include <functional>
#include <mutex>
#include <iostream>
#include <queue>
#include <sstream>
#include <condition_variable>

class ThreadCmdConsumer
{
public:
    using Guard = std::lock_guard<std::mutex>;
    using UniqueLock = std::unique_lock<std::mutex>;

    ThreadCmdConsumer()
        : inService_(),
        executionServiceThread_(std::bind(&ThreadCmdConsumer::executionServiceHandler, this))
    {
        //Wait while thread does not exist...
        UniqueLock lock(cmdQMutex_);
        while (!inService_) {
            conditional_.wait(lock);
        }
        std::cout << std::hex << this << " constructed and consumer waiting..." << std::endl;
    }

    ~ThreadCmdConsumer() {
        static std::size_t nth = 0;
        {
            UniqueLock lock(cmdQMutex_);
            std::cout << "destructing (" << std::dec << nth++ << "): " << std::hex << this << std::endl;
        }
        process([this](){
            //Note: inService_ can only be changed in consumer thread...
            inService_ = false;
            std::cout << "consumer_->inService state: " << inService_ << std::endl;
        });

        UniqueLock lock(cmdQMutex_);
        std::cout << "producer " << std::hex << this << " destructor has lock" << std::endl;
        while (inService_) {
            std::cout << "producer " << std::hex << this << " destructor in service, entering wait" << std::endl;
            conditional_.wait(lock);
            std::cout << "producer " << std::hex << this << " destructor exited wait - has lock" << std::endl;
        }
        // Join will always succeed as result of StopServiceCmd that sets inService to false 
        // (in its own thread context). Once join completes, we are certain of executionServiceHandler
        // exiting normally.
        std::cout << "produces " << std::hex << this << " destructor joining" << std::endl;
        lock.unlock();

        try {
            executionServiceThread_.join();
        }
        catch (const std::system_error& ex) {
            UniqueLock lock(cmdQMutex_);//for cout
            std::cout << "Exception during join" << ex.what() << std::endl;
            abort();
        }
    }

    void executionServiceHandler()
    {
        { //Starts the service...
            UniqueLock lock(cmdQMutex_);
            inService_ = true;
            lock.unlock();
            conditional_.notify_one();
        }

        try {
        UniqueLock lock(cmdQMutex_);
        while (inService_) {
            std::cout << "consumer " << std::hex << this << " has lock" << std::endl;
            // Catering for spurious wake-ups too, hence while...
            while (cmdQ_.empty()) {
                std::cout << "consumer " << std::hex << this << " waiting" << std::endl;
                conditional_.wait(lock);
                std::cout << "consumer " << std::hex << this << " woken" << std::endl;
            }
            //Now we have the lock, and queue most certainly not empty
            auto cmd = std::move(cmdQ_.front());
            cmdQ_.pop();
            //@@@lock.unlock(); // Don't want to be locked while executing... removed conservatively
              (cmd)();
            } 

            std::cout << "consumer " << std::hex << this << " execution complete" << std::endl;
        }
        catch(const std::exception& ex) {
           std::cerr << "Unexpected " << ex.what() << std::endl;
           abort();
        }    
        //Not in service - notify when we've left (then we can truly join...)
        conditional_.notify_one();
    }

    void process(std::function<void()>&& cmd)
    {
        UniqueLock lock(cmdQMutex_);
        std::cout << "producer " << std::hex << this << " has lock" << std::endl;
        bool notificationRequired = cmdQ_.empty();
        cmdQ_.push(move(cmd));
        if (notificationRequired) {
            std::cout << "producer " << std::hex << this << " notifying" << std::endl;
            lock.unlock();
            conditional_.notify_one();
        }
        else {
            std::cout << "producer " << std::hex << this << " not notifying" << std::endl;
        }
    }

private:
    bool inService_;
    std::queue<std::function<void()>> cmdQ_;
    std::condition_variable conditional_;
    std::mutex cmdQMutex_;
    std::thread executionServiceThread_;
};


typedef std::function<void(const std::string&)> Handler;

struct ThreadOwner
{
    ThreadCmdConsumer executor_;
    // Do it done in the context of executor....
    void doIt(const Handler& handler)
    { }
};

int main()
{
    std::cout << "Program started" << std::endl;
    //Sometimes deadlocks on thread being killed
    for (int i = 0; i < 1000; ++i) {
        auto handler = [](const std::string&){};
        {
            ThreadOwner owner;
            owner.executor_.process([&handler, &owner]() {
            owner.doIt(handler);
            });
        }
    }
}

0 个答案:

没有答案