类成员在代码的不同部分具有不同的值

时间:2019-02-12 21:35:49

标签: c++ oop templates asynchronous thread-safety

我正在尝试在我的 C ++ 应用中实现无阻塞串行通信。一个线程负责进行串行通信,我编写了一个ThreadSafeClass以在串行线程和主线程之间交换数据。这是我的代码的核心:

main.cpp

#include "serial.hpp"
#include "tsqueue.hpp"

int main(int argc, char *argv[])
{
    serial::init();
    while (true)
    {
        fgets(s);
        serial::outQueue.enqueue(std::string(s));
    }
    serial::shutdown();
    return 0;
}

tsqueue.hpp

#include <mutex>
#include <queue>

namespace tsqueue
{

template <typename T>
class ThreadSafeQueue
{
  private:
    mutable std::mutex _mtx;
    std::queue<T> _que;

  public:
    ThreadSafeQueue();
    ~ThreadSafeQueue();
    void enqueue(const T &item);
    T tryDequeue(const T &defaultValue, bool &done);
    void clear();
    bool isEmpty() const;
};

template <typename T>
ThreadSafeQueue<T>::ThreadSafeQueue() {}

template <typename T>
ThreadSafeQueue<T>::~ThreadSafeQueue() { clear(); }

template <typename T>
void tsqueue::ThreadSafeQueue<T>::enqueue(const T &item)
{
    std::lock_guard<std::mutex> lock(_mtx);
    _que.push(item);
}

template <typename T>
T tsqueue::ThreadSafeQueue<T>::tryDequeue(const T &defaultValue, bool &done)
{
    std::lock_guard<std::mutex> lock(_mtx);
    if (_que.empty())
    {
        done = false;
        return defaultValue;
    }
    else
    {
        T item = _que.front();
        _que.pop();
        done = true;
        return item;
    }
}

} // namespace tsqueue

以及串行声明/定义

serial.hpp

#include <string>
#include "tsqueue.hpp"

namespace serial
{

static tsqueue::ThreadSafeQueue<std::string> inQueue;
static tsqueue::ThreadSafeQueue<std::string> outQueue;

void init();
void shutdown();

}

serial.cpp

#include <string>    
#include "serial.hpp"
#include "tsqueue.hpp"

static std::thread _thread;

void run()
{
    while (true)
    {
        std::string str = serial::outQueue.tryDequeue(emptyStr, dequeued);
        if (dequeued) { /* Do send 'str' */ }
        if (terminationRequested) { break; }
        // Some sleep
    }
}

void serial::init()
{
    serial::inQueue.clear();
    serial::outQueue.clear();
    _thread = std::thread(run);
}

void serial::shutdown()
{
    if (_thread.joinable()) { _thread.join(); }
}

问题是,当 serial.cpp 中的串行线程tryDequeue(...)调用run()时,它总是看到空的outQueue。但是,即使在以后的时间, while 循环仍会在 main.cpp 中看到outQueue具有提供的数据。我发现使用vscode的调试工具。我是C ++的新手,但是有其他语言的经验。我在上面的代码中做错了什么? run()main()看到不同的对象吗?

编译器:g ++ 7.3.0,环境:Linux(Ubuntu 18.04)

编辑:如果我从staticinQueue的定义中删除了outQueue,则两者的链接器都会出现multiple definition错误。尽管我有适当的包括后卫。

1 个答案:

答案 0 :(得分:2)

(在修复了所有非问题之后,我终于发现了实际的问题之后,进行了大量编辑:)

问题是您有两个完全独立的outQueue 实例:main.o中的一个实例和serial.o中的一个实例(如果是.obj您在Windows上)。问题是您在标头中将它们声明为static。这样会在每个包含该标头的*.cpp /对象中得到单独的副本。

理想情况下,outQueue不是全局变量。假设它应该是一个全局变量,则可以这样解决:

serial.hpp

namespace serial
{    
    // This is a declaration of a global variable. It is harmless 
    // to include this everywhere.
    extern tsqueue::ThreadSafeQueue<std::string> inQueue;
    extern tsqueue::ThreadSafeQueue<std::string> outQueue;        
}

serial.cpp

namespace serial
{    
    // This is the actual definition of the variables. 
    // Without this you get unresolved symbols during link time
    // (but no error during compile time). If you have this in 
    // two *.cpp files you will get multiple definition linker 
    // errors (but no error at compile time). This must not be 
    // static because we want all other objects to see this as well.
    tsqueue::ThreadSafeQueue<std::string> inQueue;
    tsqueue::ThreadSafeQueue<std::string> outQueue;        
}

ThreadSafeQueue本身对我来说还不错。