C++ 在多线程进程中同步写入日志
我实现了一个记录器,所以它可以像ostream一样使用。 e、 如果有人想写日志,他可以这样做:C++ 在多线程进程中同步写入日志,c++,multithreading,logging,boost,boost-mutex,C++,Multithreading,Logging,Boost,Boost Mutex,我实现了一个记录器,所以它可以像ostream一样使用。 e、 如果有人想写日志,他可以这样做: LOG << "hello world " << 6 << 8.6 << "\n"; 重载操作符()和操作符使用std::mutex并锁定第一次使用operator()或operator你是对的operator第一感觉是,那operator()和operator就在我的头顶上。如果您想让流io操作符保持原样,可以使用某种代理对象来锁定\解锁互斥体 请
LOG << "hello world " << 6 << 8.6 << "\n";
重载操作符()和操作符使用
std::mutex
并锁定第一次使用operator()
或operator你是对的operator第一感觉是,那operator()
和operator就在我的头顶上。如果您想让流io操作符保持原样,可以使用某种代理对象来锁定\解锁互斥体
请不要把注意力放在编码风格上(尤其是swfull和危险的日志程序实现)。下面你可以找到上述想法的简要说明
template<class TLogger, class TLockObject>
class LoggerProxy
{
public:
LoggerProxy(TLogger &logger, TLockObject &lockObj)
: m_logger(logger),
m_lockGuard(lockObj)
{
}
template <typename T>
inline LoggerProxy& operator << (const T& msg)
{
m_logger.Write(msg);
return *this;
}
private:
TLogger & m_logger;
std::lock_guard<typename TLockObject> m_lockGuard;
};
//Purpose of code below is just an illustration of usage LoggerProxy class. Don't use it in production code.
class Logger
{
public:
static Logger& GetInstance()
{
static Logger instance;
return instance;
}
static std::mutex& GetLockObject()
{
static std::mutex lockObj;
return lockObj;
}
template <typename T>
inline void Write (const T& msg) {
std::cout << msg << std::endl;
}
};
#define LOG LoggerProxy<Logger, std::mutex>(Logger::GetInstance(), Logger::GetLockObject())
int main()
{
LOG << 10 << "HELLO" << 1.1;
LOG << 101 << "HELLO2" << 11.1;
return 0;
}
模板
类LoggerProxy
{
公众:
LoggerProxy(TLogger和logger、TLockObject和lockObj)
:m_记录器(记录器),
m_锁具(lockObj)
{
}
模板
内联LoggerProxy和运算符共享更少,锁定更少
如果您要在每个操作符上同步,如果您总是通过同一个“父”函数调用其他两个函数,那么互斥锁怎么会没有帮助呢?将解决您的问题。有什么问题“操作符当然我可以在不同的方法中使用互斥体,但它无法实现我想要的目标。假设我有一个线程调用:LOG,如果你能容忍部分write()
。使用低级C风格IO来open())
使用O_APPEND
创建日志文件,将所有单个日志条目合并到单个Cchar
缓冲区,并通过调用write()
将缓冲区写入日志文件。部分write()的几率
一个文件非常非常小,它只是一个日志文件。如果用户代码以意外的顺序调用没有\n
的方法,那么很好地解决了死锁问题!如果您使用这种记录器,您只需提供某种日志输入完成指示。如果用户没有编写结束指示,则进程将挂起!一点也不知道!请记住,任何应用程序都可以有多个互斥体,通过函数调用记录某些内容始终是死日志的起点。这根本不是一个有效的解决方案!唯一的解决方案是在linebreak之前执行本地缓冲区当前项。OP没有指定如何处理此问题。正如我在上面所写的-of co当然,我可以在不同的方法中使用互斥体,但它不会达到我想要的目标。假设我有一个线程来调用:LOG Added of the code sampleBrilliant!我需要在LoggerProxy上指定我的记录器(您模板化的),因为我需要额外的数据(日志级别、文件等)。但是它简单而聪明!谢谢。
template <typename T>
inline Logger& operator << (const T& msg) {
std::stringstream ss;
ss << msg;
PrintToFile(ss.str());
PrintToScreen(ss.str());
return *this;
}
Logger& Logger::operator () (const std::string& sourceFile, const std::string& funcName, int lineNumber) {
std::stringstream ss;
ss << Utilities::GetFormattedTime("%d.%m.%y %H:%M:%S") << "::" << sourceFile << "(" << lineNumber << ")::" <<
funcName << "::";
PrintToFile(level, ss.str());
PrintToScreen(level, ss.str());
return *this;
}
class Logger {
std::mutex mux;
//...
public:
Logger& operator()(/*...*/) {
mux.lock();
// write to log
}
Logger& operator<<(const string& str) {
// write to log
if(str[str.size()-1] == '\n')
mux.unlock();
}
//...
};
template<class TLogger, class TLockObject>
class LoggerProxy
{
public:
LoggerProxy(TLogger &logger, TLockObject &lockObj)
: m_logger(logger),
m_lockGuard(lockObj)
{
}
template <typename T>
inline LoggerProxy& operator << (const T& msg)
{
m_logger.Write(msg);
return *this;
}
private:
TLogger & m_logger;
std::lock_guard<typename TLockObject> m_lockGuard;
};
//Purpose of code below is just an illustration of usage LoggerProxy class. Don't use it in production code.
class Logger
{
public:
static Logger& GetInstance()
{
static Logger instance;
return instance;
}
static std::mutex& GetLockObject()
{
static std::mutex lockObj;
return lockObj;
}
template <typename T>
inline void Write (const T& msg) {
std::cout << msg << std::endl;
}
};
#define LOG LoggerProxy<Logger, std::mutex>(Logger::GetInstance(), Logger::GetLockObject())
int main()
{
LOG << 10 << "HELLO" << 1.1;
LOG << 101 << "HELLO2" << 11.1;
return 0;
}
namespace Logging {
struct SinkConcept { void commit(std::string const&); }; // documentation only
// movable log transaction (using arbitrary sink)
template <typename Sink> struct LogTx {
LogTx(Sink& s) : _sink(s) {}
LogTx(LogTx&&) = default;
unique_flag _armed;
std::ostringstream _oss;
Sink& _sink;
~LogTx() { if (_armed) _sink.commit(_oss.str()); }
template <typename T> LogTx& operator<<(T&& rhs)& { return (_oss << rhs), *this; }
template <typename T> LogTx operator<<(T&& rhs)&& { return (_oss << rhs), std::move(*this); }
};
struct NullSink { void commit(std::string const&) const {} };
template <typename Impl, bool Flush = true>
struct StreamSink {
StreamSink(Impl stream_or_ref = {}) : _sor(std::move(stream_or_ref)) {}
StreamSink(StreamSink&& rhs) : StreamSink(std::move(rhs._sor)) {}
void commit(std::string const& msg) {
std::lock_guard<std::mutex> lk(_mx);
get() << msg << "\n";
if (Flush) get() << std::flush;
}
std::ostream& get() { return _sor; }
private:
mutable std::mutex _mx;
Impl _sor; // stream convertible to ostream&
};
template <typename A, typename B> struct TeeSink { // dispatch to two sinks
A a; B b;
void commit(std::string const& msg) { a.commit(msg); b.commit(msg); }
};
// factory functions (mostly not needed in c++17 with deduction guides)
template <typename A, typename B>
TeeSink<A, B> tee(A&& a, B&& b) { return { std::forward<A>(a), std::forward<B>(b) }; }
StreamSink<std::ofstream, false> log_to(std::ofstream&& file) { return {std::move(file)}; }
StreamSink<std::reference_wrapper<std::ostream>, true> log_to(std::ostream& os) { return {os}; }
auto& log_to_stderr() {
static StreamSink<std::reference_wrapper<std::ostream>, true> s_instance { log_to(std::cerr) };
return s_instance;
}
auto& log_to_stdout() {
static StreamSink<std::reference_wrapper<std::ostream>, true> s_instance { log_to(std::cout) };
return s_instance;
}
auto& null_sink() {
static NullSink const s_instance{};
return s_instance;
}
template <typename Sink>
LogTx<Sink> make_tx(Sink& sink) { return {sink}; }
template <typename Sink>
LogTx<Sink> make_tx(Sink& sink) { return {sink}; }
#define LOG_TO(sink) (Logging::make_tx(sink) << __FILE__ << ":" << __LINE__ << "\t" << __func__ << "\t")
#ifdef NOLOGGING
#define LOG LOG_TO(Logging::null_sink())
#else
static auto _file_sink = Logging::log_to(std::ofstream("demo.log"));
static auto _both_sink = tee(_file_sink, Logging::log_to_stderr());
#define LOG LOG_TO(_both_sink)
#endif
#include <thread>
void worker(std::string id) {
while (auto r = rand()%10) {
std::this_thread::sleep_for(std::chrono::milliseconds(r));
LOG << "Ping from " << id;
}
}
int main() {
LOG << "Hello";
{
std::thread a(worker, "A"), b(worker, "B");
a.join();
b.join();
}
LOG << "Bye";
}
main.cpp:104 main Hello
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:110 main Bye
#include <functional> // for std::reference_wrapper
#include <iostream>
#include <sstream>
#include <fstream>
#include <mutex>
namespace Logging {
// utility to safely implement movable log transactions
struct unique_flag {
bool value = true;
unique_flag() = default;
unique_flag(unique_flag&& rhs) : value(rhs.value) { rhs.value = false; }
operator bool() const { return value; }
};
struct SinkConcept { void commit(std::string const&); }; // documentation only
// movable log transaction (using arbitrary sink)
template <typename Sink> struct LogTx {
LogTx(Sink& s) : _sink(s) {}
LogTx(LogTx&&) = default;
unique_flag _armed;
std::ostringstream _oss;
Sink& _sink;
~LogTx() { if (_armed) _sink.commit(_oss.str()); }
template <typename T> LogTx& operator<<(T&& rhs)& { return (_oss << rhs), *this; }
template <typename T> LogTx operator<<(T&& rhs)&& { return (_oss << rhs), std::move(*this); }
};
// Some sink models
struct NullSink { void commit(std::string const&) const {} };
template <typename Impl, bool Flush = true>
struct StreamSink {
StreamSink(Impl stream_or_ref = {}) : _sor(std::move(stream_or_ref)) {}
StreamSink(StreamSink&& rhs) : StreamSink(std::move(rhs._sor)) {}
void commit(std::string const& msg) {
std::lock_guard<std::mutex> lk(_mx);
get() << std::move(msg);
if (Flush)
get() << std::endl;
else
get() << "\n";
}
std::ostream& get() { return _sor; }
private:
mutable std::mutex _mx;
Impl _sor; // stream convertible to ostream&
};
template <typename A, typename B> struct TeeSink { // dispatch to two sinks
A a; B b;
void commit(std::string const& msg) { a.commit(msg); b.commit(msg); }
};
// factory functions (mostly not needed in c++17 with deduction guides)
template <typename A, typename B>
TeeSink<A, B> tee(A&& a, B&& b) { return { std::forward<A>(a), std::forward<B>(b) }; }
StreamSink<std::ofstream, false> log_to(std::ofstream&& file) { return {std::move(file)}; }
StreamSink<std::reference_wrapper<std::ostream>, true> log_to(std::ostream& os) { return {os}; }
StreamSink<std::reference_wrapper<std::ostream>, true>& log_to_stderr() {
static StreamSink<std::reference_wrapper<std::ostream>, true> s_instance { log_to(std::cerr) };
return s_instance;
}
StreamSink<std::reference_wrapper<std::ostream>, true>& log_to_stdout() {
static StreamSink<std::reference_wrapper<std::ostream>, true> s_instance { log_to(std::cout) };
return s_instance;
}
NullSink const& null_sink() {
static NullSink const s_instance{};
return s_instance;
}
template <typename Sink>
LogTx<Sink> make_tx(Sink& sink) { return {sink}; }
}
#define LOG_TO(sink) (Logging::make_tx(sink) << __FILE__ << ":" << __LINE__ << "\t" << __func__ << "\t")
#ifdef NOLOGGING
#define LOG LOG_TO(Logging::null_sink())
#else
static auto _file_sink = Logging::log_to(std::ofstream("demo.log"));
static auto _both_sink = tee(_file_sink, Logging::log_to_stderr());
#define LOG LOG_TO(_both_sink)
#endif
#include <thread>
void worker(std::string id) {
while (auto r = rand()%10) {
std::this_thread::sleep_for(std::chrono::milliseconds(r));
LOG << "Ping from " << id;
}
}
int main() {
LOG << "Hello";
{
std::thread a(worker, "A"), b(worker, "B");
a.join();
b.join();
}
LOG << "Bye";
}