我正在开发一个多线程C ++应用程序,并开发了一个用于记录的模块。日志记录模块是一个静态类,我使用Logger :: Log(字符串文件,字符串消息)调用,它使用pair<string*,string*>
填充静态队列。队列本身是queue<<pair<string*,string*>*>
。一切都保存为指针,因为我试图避免垃圾收集,并且相信指针变量需要特定的删除来释放内存。
现在,当其中一个线程想要记录某些内容时,它会调用Log方法,而该方法又会附加到队列的末尾。
另一个线程在队列中运行,弹出项目并将它们写入指定的文件。
出于某种原因,写入文件的一些文本已损坏,因为我正在丢失部分文件或邮件的结尾。
例如,如果我调用Log(“file”,“这是我的消息”),在Log方法中我会在前面添加一个时间戳,并创建一个新字符串,因为我认为原始字符串可能会被覆盖,但是它仍然发生。 问题是在某些情况下,写入文件的是时间戳,加上只有消息的结尾。
这是Logger类的完整代码:
#include "Logger.h"
queue<pair<string*, string*>*> Logger::messages;
boost::mutex Logger::loggerLock;
void Logger::CleanOldFiles(vector<string> files){
for (vector<string>::iterator it = files.begin(); it != files.end(); ++it) {
string filePath = boost::filesystem::current_path().string() + "\\" + *it;
int result = remove(filePath.c_str());
}
}
void Logger::Init() {
Logger::messages = queue<pair<string*, string*>*>();
boost::thread workerThread(Logger::Process);
//workerThread.start_thread();
}
void Logger::RawLog(string file, string message) {
loggerLock.lock();
string *f = new string(file);
string *m = new string(message + "\n");
messages.push(new pair<string*, string*>(f, m));
loggerLock.unlock();
}
void Logger::Log(string file, string message) {
loggerLock.lock();
string *f = new string(file);
string *m = new string(Functions::CurrentTime() + " (" + boost::lexical_cast<string>(boost::this_thread::get_id()) + "): " + message.c_str() + "\n");
messages.push(new pair<string*, string*>(f, m));
loggerLock.unlock();
}
void Logger::Process() {
while (true) {
if (Logger::messages.size() == 0) {
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
continue;
}
loggerLock.lock();
pair<string*, string*> *entry = messages.front();
messages.pop();
loggerLock.unlock();
ofstream file(boost::filesystem::current_path().string() + "\\" + *(entry->first), ofstream::binary | ofstream::app);
file.write(entry->second->c_str(), entry->second->length());
file.close();
delete entry->first;
delete entry->second;
delete entry;
//cout << entry->second;
}
}
我希望自己足够清楚......
我不明白为什么会发生这种情况,有人能给我一些关于如何避免这种情况的提示吗?
提前致谢。
答案 0 :(得分:1)
Logger::Log
必须为MT安全,否则您可以获得两个或多个线程同时尝试记录某些内容。使其成为MT安全的最简单方法是mutex
。
答案 1 :(得分:1)
答案 2 :(得分:0)
if (Logger::messages.size() == 0) {
因为messages
不是线程安全的,所以当你不持有锁时,你不能调用它上面的任何函数。此外,您仍然缺少对delete
的{{1}}次来电。
你总是可以这样做:
string*
然而,供将来参考:
1)不要以这种方式使用指针。只需使用成对字符串的队列。指针不会给你买任何东西。
2)使用一些理智的同步机制,如条件变量。请勿使用void Logger::Process()
{
while (true)
{
loggerLock.lock();
if (Logger::messages.size() == 0)
break;
loggerLock.unlock();
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
}
pair<string*, string*> *entry = messages.front();
messages.pop();
loggerLock.unlock();
ofstream file(boost::filesystem::current_path().string() + "\\" +
*(entry->first), ofstream::binary | ofstream::app);
file.write(entry->second->c_str(), entry->second->length());
delete entry->first;
delete entry->second;
file.close();
}
进行同步。
3)使用范围锁和RAII,这样你就不会忘记解锁。
答案 3 :(得分:0)
我为所有试图帮助我的工作道歉,但我纠正了这个问题。这实际上没有内存损坏问题,我的假设让我搜索没有问题的东西。
问题出在调用线程上,在表单上我创建了日志字符串。我以错误的方式连接字符串......该死的其他编程语言;)
我正在做一些事情,比如Logger :: Log(“Message:”+ integerVariable),这实际上是将字符串向右移动(至少我相信它就是它正在做的事情)。当我将所有这些变量转换为字符串时,一切都开始工作了。无论如何,谢谢你的帮助。