【问题标题】:Synchronization error in consumer/producer style logger消费者/生产者样式记录器中的同步错误
【发布时间】:2019-07-31 21:47:48
【问题描述】:

我看到很多这样的问题,但没有一个对我有帮助,所以我发布了我的。

我为多线程日志制作了一个记录器。 只有一个线程(在创建第一个记录器时创建。)处理来自多个线程的日志消息,并且使用条件变量同步缓冲区。一种用于push(),一种用于pop()

它看起来很好,但是当短时间内收到太多消息时,它就不起作用了。

这是来自 xutility 的异常消息。

Exception thrown: read access violation.
_Pnext was 0xFFFFFFFFFFFFFFFF.

这是我的代码。

logger.h

class ConsoleLogger
{
private:
    static Buffer<std::string, 1000> buffer;
    uint32_t curLine = 0;
    static bool instantiated;
    std::mutex logLock;
    std::string prefix;
    std::ostringstream logStream;

    // creates log thread one very first logger calls constructor
    void createLogThread();
    // keeps logging incoming logs
    static void logThreadFunc();

public:
    ConsoleLogger();
    void setPrefix(const std::string_view& newPrefix);

    template<typename T, typename... Types>
    void log(T log1, Types... logs) {
        {
            std::scoped_lock<std::mutex> lock(logLock);
            noLog = false;

            logStream.clear();
            logStream.str("");
            logStream << prefix << "#" << curLine << "\t" << std::this_thread::get_id() << "\t";
            ++curLine;

            logMessage(log1, logs...);
        }
    }

private:
    void logMessage() {
        logStream << "\n";

        std::string* str = new std::string{ logStream.str() };

        buffer.push(str);
    }

    template<typename T, typename... Types>
    void logMessage(T log1, Types... logs) {
        logStream << log1;
        logMessage(logs...);
    }
};

logger.cpp

Buffer<std::string, 1000> ConsoleLogger::buffer;

void ConsoleLogger::logThreadFunc() {
    while (true) {
        std::string* log = buffer.pop();
        if (log == nullptr) {
            std::cerr << "log was nullptr" << std::endl;
            continue;
        }
        std::cout << *log;
        delete log;          <-------------throws exception

    }
}

buffer.h

template<class T, uint64_t Qsize>
class Buffer
{
private:
    T* arrayPtr[Qsize];
    uint64_t head;
    uint64_t tail;
    static const uint64_t mask = Qsize - 1;

    std::condition_variable condEmpty;
    std::condition_variable condOverflow;
    std::mutex mtx;
public:
    Buffer();

    void push(T* x);
    T* pop();
};

template <class T, uint64_t Qsize>
Buffer<T, Qsize>::Buffer() : head(0),
                             tail(0)
{}

template <class T, uint64_t Qsize>
void Buffer<T, Qsize>::push(T* x)
{
    std::unique_lock<std::mutex> lock(mtx);

    condOverflow.wait(lock, [this]() {
        return tail + Qsize > head;
        });

    arrayPtr[head++ & mask] = x;

    lock.unlock();
    condEmpty.notify_one();
}

template <class T, uint64_t Qsize>
T* Buffer<T, Qsize>::pop()
{
    std::unique_lock<std::mutex> lock(mtx);

    condEmpty.wait(lock, [this]() {
        return tail < head;
        });

    T* x = arrayPtr[tail++ & mask];

    lock.unlock();
    condOverflow.notify_one();

    return x;
}

过去几天我一直在尝试解决此问题,但我不知道为什么会发生这种情况。 我认为它一直在尝试访问已删除的内存。 我在没有删除的情况下进行了测试,它记录了已经打印的相同消息。

任何建议或帮助将不胜感激。

【问题讨论】:

  • 看起来你没有正确初始化arrayPtr 的元素,所以pop 在没有可用的情况下似乎未定义
  • 如果第三方库可以接受,Boost::log 是多线程环境的不错选择。
  • arrayPtr[head++ &amp; mask] 应该做什么?你确定你的意思不是arrayPtr[head++ % Qsize]Buffer::pop 中的arrayPtr[tail++ &amp; mask] 也是如此)?

标签: c++ multithreading logging producer-consumer condition-variable


【解决方案1】:

您应该明确地将 arrayPtr 的元素清空

template <class T, uint64_t Qsize>
Buffer<T, Qsize>::Buffer() : head(0),
                             tail(0)
{
    for (uint64_t i = 0; i < Qsize; i++)
        arrayPtr[i] = nullptr;
}


【讨论】:

    猜你喜欢
    • 2017-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-13
    • 2018-01-07
    相关资源
    最近更新 更多