我正在为我的引擎进行多线程日志记录。但我有麻烦使它成为MT。问题是,即使我将我的记录器对象深度复制到本地变量,我也会遇到文件问题。因为两个线程将同时写入同一个文件,这将构成一个混乱。这是我的记录器类:
class Logger {
public:
typedef std::vector<LogListener *> ListenerList;
private:
ListenerList listeners;
boost::mutex mutex;
public:
Logger();
~Logger();
Logger * write(const String &line);
};
Logger * Logger::write(const String &text) {
if(listeners.empty()) return this;
boost::unique_lock<boost::mutex> lock(mutex);
for(ListenerList::iterator i = listeners.begin(); i != listeners.end(); ++i) {
(*i)->write(text);
}
return this;
}
class FileLogListener : public LogListener {
std::ofstream stream;
public:
FileLogListener(const String &str) : stream(str.c_str(), std::ios::out | std::ios::app) { }
void write(const String &text) {
stream << text << std::endl;
}
};
现在假设(避免使用互斥锁):
//Thread 1
void func1() {
Logger * log = new Logger;
log->addListener("file.txt");
log->write("Thread 1 Test");
}
//Thread 2
void func2() {
Logger * log = new Logger;
log->addListener("file.txt");
log->write("Thread 2 test");
}
int main() {
boost::thread t1(&func1);
boost::thread t2(&func2);
t1.join();
t2.join();
return 0;
}
“file.txt”变得一团糟。
编辑:目前,我正在阅读和观看有关多线程的讲座,以便更好地理解它。
编辑:以上记录器有效。
提前致谢,
Gasim Gasimzada
答案 0 :(得分:3)
要么不写入同一文件,要么串行写入(例如使用锁定)。
答案 1 :(得分:2)
我写完article about minimalist logger之后。它是一个可以在MT环境中使用的单头文件简单记录器。它满足了我在许多项目中的日志记录需求
如果您想使用记录器,可以查看方法
简而言之:它只是通过boost :: mutex
锁定对文件的访问答案 2 :(得分:1)
你应该给log4cxx一个机会。
我只知道这个记录器框架的java和c#版本,但它们很棒。
并且您不必担心多线程,并且可以专注于您的实际工作。
只需更改配置文件,您也可以轻松地将日志目标从文件更改为sql甚至udp。
http://logging.apache.org/log4cxx/index.html
HTH
答案 3 :(得分:0)
为每个文件创建一个单独的Logger
对象,并在其write方法中保存一个互斥锁。
答案 4 :(得分:0)
在Ubuntu Linux中,可能正在运行一个syslog守护程序。利用它,不要重新发明轮子。
答案 5 :(得分:0)
我认为你遇到的困难是你有多个文件lisener都指向同一个文件。这意味着您没有捕获所有尝试写入文件的互斥锁。另一种方法是向你的lisener添加一个静态互斥:
class FileLogListener : public LogListener {
std::ofstream stream;
static boost::mutex m_mutex; //same mutex for all writes to (any) file
public:
FileLogListener(const String &str) : stream(str.c_str(), std::ios::out | std::ios::app) { }
void write(const String &text) {
boost::mutex::scoped_lock lock(m_mutex);//lock the mutex
stream << text << std::endl;
}
};
//somewhere in you cpp file, initialize the mutex
boost::mutex FileLogListener::m_mutex;
我还认为您设置的记录器过于复杂(可能您有超出此问题范围的原因)。您将来可能遇到的一个问题是,当您想要优化代码时,可能会发现很难关闭注销。
万一你发现它有用,我想我会发一个替代品,
#include <iostream>
#include <fstream>
#include <boost/thread.hpp>
template<class T>
struct threadsafe
{
static boost::mutex m_mutex;
static void print(const std::string& msg)
{
boost::mutex::scoped_lock lock(m_mutex);
T::print(msg);
}
};
struct file_print
{
static std::ofstream stream;
static void print(const std::string& msg)
{
stream<<msg;
}
};
struct console_print
{
static void print(const std::string& msg)
{
std::cout<<msg<<std::endl;
}
};
struct dont_log
{
static void print(const std::string& msg)
{}
};
//somewhere in the library c++ file
template<class T>
boost::mutex threadsafe<T>::m_mutex;
然后您可以像这样使用记录器:
template<class logger = dont_log>
struct my_class
{
void run()
{
logger::print("msg\n");
}
};
std::ofstream file_print::stream("test.txt");
int
main (int ac, char **av)
{
my_class< threadsafe<file_print> > c;
//or log to the console
//my_class< threadsafe<console_print> > c;
//or don't log at all (by default)
//my_class<> c;
c.run();
}
此记录器的优势在于,如果您(或您的用户)决定不再需要记录,他们只需将模板参数切换为dont_log
即可。由于print函数是静态的,因此编译器应删除对空函数的所有引用,并优化所有日志记录。