多线程日志帮助
Posted
技术标签:
【中文标题】多线程日志帮助【英文标题】:Multithreaded Logging Help 【发布时间】:2011-07-19 09:31:07 【问题描述】:我正在为我的引擎开发多线程日志记录。但是我在制作 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”变得一团糟。
编辑:目前,我正在阅读和观看有关多线程的讲座以更好地理解它。
编辑:上面的记录器有效。
提前致谢, 加西姆加西姆扎达
【问题讨论】:
@littleadv: Ubuntu Linux 您的线程不起作用,因为您在func1
和 func2
中使用了不同的互斥锁。您希望您的互斥锁在处理写入的类中,然后在写入周围有一个作用域锁(以便文件的每个锁都引用同一个互斥锁)。或者在func1
和func2
中有一个指向同一个互斥锁的指针
我忘记编辑了。我现在修好了。
【参考方案1】:
要么不写入同一个文件,要么将写入序列化(例如通过使用锁)。
【讨论】:
你的意思是文件锁?还是线程锁?如果您的意思是文件锁,您知道我可以在其中实现它的库吗?谢谢 @Gasim:Boost 进程间库执行文件锁定:boost.org/doc/libs/1_47_0/doc/html/interprocess/… 谢谢汤姆。我现在就试试。 这个库不保证文件锁在多线程的一个进程中不会崩溃。 ://【参考方案2】:一旦我写了这个article about minimalist logger。它是一个可以在 MT 环境中使用的单头文件简单记录器。它满足了我在很多项目中的日志记录需求
如果你想使用你的记录器,你可以检查方法
简而言之:它只是通过 boost::mutex 锁定对文件的访问
【讨论】:
【参考方案3】:你应该给 log4cxx 一个机会。
我只知道这个记录器框架的 java 和 c# 版本,但它们很棒。
而且您不必担心多线程,可以专注于您的实际工作。
您还可以轻松地将日志目标从文件更改为 sql 甚至 udp,只需更改配置文件即可。
http://logging.apache.org/log4cxx/index.html
第
【讨论】:
我已阅读link。并检查了那里发布的所有库(log4cxx 也在那里)。这些库对于日志系统来说是巨大的,它们有太多我不想要的特性。这就是为什么我试图用 MT 编写我自己的非常轻量级的记录器类。【参考方案4】:为每个文件创建一个单独的Logger
对象,并在其写入方法中保存一个互斥锁。
【讨论】:
【参考方案5】:在 Ubuntu Linux 中,可能有一个 syslog 守护程序正在运行。利用它,不要重新发明***。
【讨论】:
利用它是什么意思?【参考方案6】:我认为你遇到的困难是你有多个文件监听器都指向同一个文件。这意味着您没有一个互斥锁来捕获对文件的所有尝试写入。解决此问题的一种方法是向您的 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 函数是静态的,编译器应该删除对空函数的所有引用,并优化所有日志记录。
【讨论】:
这是一个好主意,但是通过我的方法,我可以在我的记录器类中有一个空的侦听器列表,并且不会记录任何内容。这个记录器还没有完成,因为我需要添加过滤器。但现在不需要,因为我正在将所有内容都写入一个文件和控制台以上是关于多线程日志帮助的主要内容,如果未能解决你的问题,请参考以下文章