我一直在四處尋找各種方法來解決線程安全日誌問題,但我還沒有看到任何相當類似的東西,所以我不知道是否因爲作爲C++的一個完整的新手而沒有注意到它是某種可怕的東西,線程和iostreams。它似乎在我已經完成的基本測試中起作用。我的線程安全日誌類的方法很糟糕嗎?
基本上我有一個Log類(創意,我知道...),它具有爲標準操縱器設置的操作符< <,所以我可以快活地傳入任何我想要的東西。
但是,我知道是這樣的:
std::cout << "Threads" << " will" << " mess" << " with" << "this." << std::endl;
將有可能得到交錯當多個線程編寫來清點(或其他地方登錄的ostream點)。所以,我創建了一些特定於日誌類操縱,讓我做到這一點:
Log::log << lock << "Write" << " what" << " I" << " want" << std::endl << unlock;
我只是想知道,如果這是一個固有的可怕想法,記住,我願意接受軸承用戶的Log類需要通過'鎖定'和'解鎖'進行處理。我認爲使'std :: endl'自動解鎖,但這似乎會造成更多的麻煩......我認爲散佈的使用應該在測試中出現,但是如果任何人都可以看到一種方法來進行編譯,時間錯誤,那很好。
我也很感激任何建議,使我的代碼更清潔。
以下是該課堂的簡化版本,用於演示目的;整個事情有更多的構造函數採用類似文件名的東西,所以與問題無關。
#include <iostream>
#include <thread>
#include <fstream>
class Log{
public:
//Constructors
Log(std::ostream & os);
// Destructor
~Log();
// Input Functions
Log & operator<<(const std::string & msg);
Log & operator<<(const int & msg);
Log & operator<<(std::ostream & (*man)(std::ostream &)); // Handles manipulators like endl.
Log & operator<<(std::ios_base & (*man)(std::ios_base &)); // Handles manipulators like hex.
Log & operator<<(Log & (*man)(Log &)); // Handles custom Log manipulators like lock and unlock.
friend Log & lock(Log & log); // Locks the Log for threadsafe output.
friend Log & unlock(Log & log); // Unlocks the Log once threadsafe output is complete.
private:
std::fstream logFile;
std::ostream & logStream;
std::mutex guard;
};
// Log class manipulators.
Log & lock(Log & log); // Locks the Log for threadsafe output.
Log & unlock(Log & log); // Unlocks the Log once threadsafe output is complete.
void threadUnsafeTask(int * input, Log * log);
void threadSafeTask(int * input, Log * log);
int main(){
int one(1), two(2);
Log log(std::cout);
std::thread first(threadUnsafeTask, &one, &log);
std::thread second(threadUnsafeTask, &two, &log);
first.join();
second.join();
std::thread third(threadSafeTask, &one, &log);
std::thread fourth(threadSafeTask, &two, &log);
third.join();
fourth.join();
return 0;
}
void threadUnsafeTask(int * input, Log * log){
*log << "Executing" << " thread '" << *input << "', " << "expecting " << "interruptions " << "frequently." << std::endl;
}
void threadSafeTask(int * input, Log * log){
*log << lock << "Executing" << " thread '" << *input << "', " << "not expecting " << "interruptions." << std::endl << unlock;
}
// Constructors (Most left out as irrelevant)
Log::Log(std::ostream & os): logFile(), logStream(logFile), guard(){
logStream.rdbuf(os.rdbuf());
}
// Destructor
Log::~Log(){
logFile.close();
}
// Output Operators
Log & Log::operator<<(const std::string & msg){
logStream << msg;
return *this;
}
Log & Log::operator<<(const int & msg){
logStream << msg;
return *this;
}
Log & Log::operator<<(std::ostream & (*man)(std::ostream &)){
logStream << man;
return *this;
}
Log & Log::operator<<(std::ios_base & (*man)(std::ios_base &)){
logStream << man;
return *this;
}
Log & Log::operator<<(Log & (*man)(Log &)){
man(*this);
return *this;
}
// Manipulator functions.
Log & lock(Log & log){
log.guard.lock();
return log;
}
Log & unlock(Log & log){
log.guard.unlock();
return log;
}
它爲我在Ubuntu12.04克++編譯:
g++ LogThreadTest.cpp -o log -std=c++0x -lpthread
相關,以使定製操縱是無恥地從here但那兒剽竊不要怪他們我的無能copypasta位。
這是一個錯誤在這裏不使用RAII。 –
恕我直言,一個無鎖FIFO將是一個更好的主意在這裏..以某種方式線程暫停記錄不吸引力。 – vrdhn
依靠用戶鎖定和解鎖東西是不可靠的。一種選擇是讓您的日誌文件同時從用戶那裏獲取消息,並通過將它們放入單個隊列並在單獨的線程中運行它們來序列化它們。所以從多用戶的角度來看,這些呼叫是非阻塞的,但實際上沒有交織。參見[Herb Sutter的這篇演講](http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Herb-Sutter-Concurrency-and-Parallelism)瞭解更多信息。我經歷了實現他的併發對象包裝的工作版本的練習。 – juanchopanza