我想编写一个基本的线程安全记录器,具有类似 cout 的接口(interface)。我想出了以下类(class)设计。这绝对不是最好的设计,因为如果使用错误,它可能会陷入死锁,如 int main()
所示。
#include <iostream>
#include <sstream> // for string streams
#include <mutex>
#include <memory>
typedef std::ostream&(*endl)(std::ostream&);
class BasicLogger {
public:
enum SEVERITY {
CRITICAL,
ERROR,
WARNING
};
explicit BasicLogger(SEVERITY _s): s(_s) {
streamMutex.lock();
logStream.reset(new std::ostringstream);
}
~BasicLogger() {
std::cout << logStream->str();
streamMutex.unlock();
}
std::ostringstream& operator<< (const endl eof) {
(*logStream) << eof;
return (*logStream);
}
template<typename T>
std::ostringstream& operator<< (const T& obj) {
(*logStream) << obj;
return (*logStream);
}
static std::unique_ptr<std::ostringstream> logStream;
BasicLogger(const BasicLogger&) = delete;
BasicLogger& operator=(const BasicLogger&) = delete;
private:
SEVERITY s; //TODO
static std::mutex streamMutex;
};
/*=======================================================*/
std::unique_ptr<std::ostringstream> BasicLogger::logStream;
std::mutex BasicLogger::streamMutex;
/*=======================================================*/
int main() {
int a = 9;
int b = 8;
// BasicLogger l(BasicLogger::ERROR); //Deadlock situation
BasicLogger(BasicLogger::ERROR) << "Linux" << " " << a << " " << b << std::endl;
BasicLogger(BasicLogger::ERROR) << "MyMachine";
BasicLogger(BasicLogger::ERROR) << std::endl;
}
最佳答案
您在构造函数中锁定互斥锁,并在析构函数中解锁它。
因此,不可能同时创建多个 BasicLogger
实例。
BasicLogger l(BasicLogger::ERROR);
将调用构造函数,从而获取锁。在 l
超出范围之前,不会调用析构函数,这意味着互斥体保持锁定状态,直到 l
超出范围。
如果您尝试构造另一个 BasicLocker
,您的构造函数会尝试获取在 l
被销毁之前不可用的锁,从而导致死锁。
当您创建临时 BasicLogger
实例时,BasicLogger(BasicLogger::ERROR)
会调用构造函数,使用该对象,然后立即销毁。因此,被锁定的互斥体被解锁。
由于您要为每个 BasicLogger
实例创建独立的 std::stringstream
,因此您需要一个锁来保护对 std::stringstream
的访问,因此多个线程可以写入同一个记录器。因此,每个实例都应该持有一个互斥锁。
您还需要一个静态互斥体来保护对 std::cout
的同时访问。日志打印时获取锁,立即释放。当然,这要求所有对 std::cout
的访问都通过 BasicLogger
进行。
class BasicLogger {
public:
BasicLogger() = default;
~BasicLogger() {
std::lock_guard<std::mutex> lLock(localMutex); /* the order of locking is important */
std::lock_guard<std::mutex> gLock(globalMutex);
std::cout << stream.str();
}
/* TODO: satisfying the rule of 5 */
template <class T>
BasicLogger& operator<< (const T& item) {
std::lock_guard<std::mutex> lLock(localMutex);
stream << item;
return *this;
}
private:
std::ostringstream stream;
std::mutex localMutex;
static std::mutex globalMutex;
};
关于c++ - 使以下记录器实现线程安全的更好方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55210628/