多线程日志帮助

时间:2011-07-19 09:31:08

标签: c++ multithreading logging boost g++

我正在为我的引擎进行多线程日志记录。但我有麻烦使它成为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

6 个答案:

答案 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函数是静态的,因此编译器应删除对空函数的所有引用,并优化所有日志记录。