【发布时间】: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++ & mask]应该做什么?你确定你的意思不是arrayPtr[head++ % Qsize](Buffer::pop中的arrayPtr[tail++ & mask]也是如此)?
标签: c++ multithreading logging producer-consumer condition-variable