2017-04-15 36 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'; 
}