2017-04-15 1802 views
0

所以我有多个线程通过调用Log :: write方法写入同一个文件。C++:在多线程程序中写入文件

class Log 
{ 
private: 
    ofstream log; 
    string file_path; 
public: 
    Log(string); 
    void write(string); 
}; 
Log::Log(string _file_path) 
{ 
    file_path=_file_path; 
} 
void Log::write(string str) 
{ 
    EnterCriticalSection(&CriticalSection); 
    log.open(file_path.c_str(),std::ofstream::app); 
    log<<str+'\n'; 
    log.close(); 
    LeaveCriticalSection(&CriticalSection); 
} 

线程是否会同时调用同一个对象的Log :: write方法是否安全?

+1

是。考虑使用RAII锁定/解锁临界区以使其异常安全。考虑使用C++标准锁定。考虑使用单独的日志记录线程和消息队列来缩小锁定瓶颈。注意打开文件(在MS Windows下)是一个非常昂贵的操作 - 考虑将日志记录类更改为单例,并且只打开一次文件。 –

+1

考虑使用第三方日志记录库 – ZivS

回答

1

你的代码很浪费,不遵循C++习惯用法。

从结尾开始:是的,write是线程安全的,因为win32 CRITICAL_SECTION可以保护它免受并发修改。

虽然:

  1. 为什么打开,每次关闭流?这是非常浪费的事情。在构造函数中打开流并将其打开。析构函数将处理关闭流。

  2. 如果你想使用Win32临界区,至少使其RAII安全。创建一个包含对临界区的引用的类,将其锁定在构造函数中并在析构函数中解锁它。这种方式,即使抛出异常 - 你保证锁将被解锁。

  3. 无论如何,CriticalSection的减速度在哪里?它应该是Log的成员。你知道吗std::mutex

  4. 你为什么按价值传递字符串?这是非常无效的。然后通过const引用传递。

  5. 您对一些变量(file_path)使用snake_case,而对其他变量使用snake_case(CriticalSection)。使用相同的约定。

  6. str从来都不是一个字符串变量的好名字,并且文件流不是日志。是真正的日志记录的事情。 logger是一个更好的名字。在我的更正中,只是将其命名为m_file_stream

更正代码:

class Log 
{ 

private: 

    std::mutex m_lock; 
    std::ofstream m_file_stream; 
    std::string m_file_path; 

public: 

    Log(const std::string& file_path); 
    void write(const std::string& log); 
}; 

Log::Log(const std::string& file_path): 
    m_file_path(file_path) 
{ 
    m_file_stream.open(m_file_path.c_str()); 
    if (!m_file_stream.is_open() || !m_file_stream.good()) 
    { 
     //throw relevant exception. 
    } 
} 

void Log::write(const std::string& log) 
{ 
    std::lock_guard<std::mutex> lock(m_lock); 
    m_file_stream << log << '\n'; 
}