Члены класса имеют разные значения в разных частях кода - PullRequest
0 голосов
/ 13 февраля 2019

Я пытаюсь реализовать неблокирующую последовательную связь в моем приложении 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(); }
}

Проблемаэто когда tryDequeue(...) вызывается последовательным потоком run() в serial.cpp , он всегда видит пустой outQueue.Однако , в то время как цикл все еще видит outQueue в main.cpp с предоставленными данными, даже в более поздние времена.Я узнал, что с помощью инструментов отладки vscode.Я новичок в C ++, но опыт работы на других языках.Что я делаю не так в приведенном выше коде?run() и main() видят разные объекты?

Компилятор: g ++ 7.3.0, Среда: Linux (Ubuntu 18.04)

Редактировать : Если я удаляю static из определений inQueue и outQueue, я получаю multiple definition ошибку компоновщика для обоих.Хотя у меня есть подходящие охранники.

1 Ответ

0 голосов
/ 13 февраля 2019

(Сильно отредактировано после того, как все не-проблемы были устранены, и после того, как я, наконец, обнаружил, в чем была настоящая проблема:)

Проблема в том, что у вас есть два совершенно разных экземпляра 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 выглядит нормально для меня.

...