muduo库里面的日志使方法如下

这里定义了一个宏
#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \
muduo::Logger(__FILE__, __LINE__).stream()
返回的stream重载了一系列的运算符,使用方法如下
LOG_INFO<<“info ...”; // 使用方式
上面那句其实就是
muduo::Logger(__FILE__, __LINE__).stream()<<“info”;

调用过程实际是如下所示的

Logger => Impl => LogStream => operator<< FixedBuffer => g_output => g_flush

muduo库里面的日志类封装相关的文件为Logging.h,Logging.cc,LogSrtream.h,LogSrtream.cc,附带一个StringPiece.h文件,几个文件的注解如下所示:

Logging.h

//日志输出
#ifndef MUDUO_BASE_LOGGING_H
#define MUDUO_BASE_LOGGING_H #include <muduo/base/LogStream.h>
#include <muduo/base/Timestamp.h> namespace muduo
{ class Logger
{
public:
//日志级别
enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
NUM_LOG_LEVELS,//级别个数6
}; // compile time calculation of basename of source file
class SourceFile
{
public:
template<int N>
inline SourceFile(const char (&arr)[N])
: data_(arr),
size_(N-1)
{
const char* slash = strrchr(data_, '/'); // builtin function
if (slash)
{
data_ = slash + 1;
size_ -= static_cast<int>(data_ - arr);
}
} explicit SourceFile(const char* filename)
: data_(filename)
{
const char* slash = strrchr(filename, '/');
if (slash)
{
data_ = slash + 1;
}
size_ = static_cast<int>(strlen(data_));
} const char* data_;
int size_;
};
//四个构造函数
Logger(SourceFile file, int line);
Logger(SourceFile file, int line, LogLevel level);
Logger(SourceFile file, int line, LogLevel level, const char* func);
Logger(SourceFile file, int line, bool toAbort);
~Logger(); LogStream& stream() { return impl_.stream_; } static LogLevel logLevel();
static void setLogLevel(LogLevel level); typedef void (*OutputFunc)(const char* msg, int len);
typedef void (*FlushFunc)();
static void setOutput(OutputFunc);
static void setFlush(FlushFunc); private:
//Loger类的内部嵌套类
//Impl类主要是负责日志的格式化
class Impl
{
public:
typedef Logger::LogLevel LogLevel;
//构造函数
Impl(LogLevel level, int old_errno, const SourceFile& file, int line);
//格式化时间函数
void formatTime();
void finish(); Timestamp time_;//Timestamp时间戳
LogStream stream_;//LogStream类对象成员
LogLevel level_;//日志级别
int line_;//行号
SourceFile basename_;
}; Impl impl_;//Impl类对象成员 }; extern Logger::LogLevel g_logLevel; inline Logger::LogLevel Logger::logLevel()
{
return g_logLevel;//日志级别
}
//日志输出宏,输出在哪个文件? 哪一行? 哪个函数? 哪种级别?
//无名对象所在语句执行后就立即被析构,然后调用析构函数将缓冲区的内容分输出
// muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()返回的是一个LogStream对象
#define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \
muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()
#define LOG_DEBUG if (muduo::Logger::logLevel() <= muduo::Logger::DEBUG) \
muduo::Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream()
#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \
muduo::Logger(__FILE__, __LINE__).stream()
#define LOG_WARN muduo::Logger(__FILE__, __LINE__, muduo::Logger::WARN).stream()
#define LOG_ERROR muduo::Logger(__FILE__, __LINE__, muduo::Logger::ERROR).stream()
#define LOG_FATAL muduo::Logger(__FILE__, __LINE__, muduo::Logger::FATAL).stream()
//false表示不会退出程序
#define LOG_SYSERR muduo::Logger(__FILE__, __LINE__, false).stream()//ERROR
//true表示会退出程序
#define LOG_SYSFATAL muduo::Logger(__FILE__, __LINE__, true).stream()//FATAL const char* strerror_tl(int savedErrno); // Taken from glog/logging.h
//
// Check that the input is non NULL. This very useful in constructor
// initializer lists. #define CHECK_NOTNULL(val) \
::muduo::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val)) // A small helper for CHECK_NOTNULL().
template <typename T>
T* CheckNotNull(Logger::SourceFile file, int line, const char *names, T* ptr) {
if (ptr == NULL) {
Logger(file, line, Logger::FATAL).stream() << names;
}
return ptr;
} } #endif // MUDUO_BASE_LOGGING_H

Logging.cc

#include <muduo/base/Logging.h>

#include <muduo/base/CurrentThread.h>
#include <muduo/base/StringPiece.h>
#include <muduo/base/Timestamp.h> #include <errno.h>
#include <stdio.h>
#include <string.h> #include <sstream> namespace muduo
{ /*
class LoggerImpl
{
public:
typedef Logger::LogLevel LogLevel;
LoggerImpl(LogLevel level, int old_errno, const char* file, int line);
void finish(); Timestamp time_;
LogStream stream_;
LogLevel level_;
int line_;
const char* fullname_;
const char* basename_;
};
*/ __thread char t_errnobuf[512];
__thread char t_time[32];
__thread time_t t_lastSecond; const char* strerror_tl(int savedErrno)
{
return strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf);
}
//初始化日志级别
Logger::LogLevel initLogLevel()
{//获取环境变量
if (::getenv("MUDUO_LOG_TRACE"))//如果获取到了这个环境变量
return Logger::TRACE;//日志级别为TRACE
else if (::getenv("MUDUO_LOG_DEBUG"))//如果获取到了这个环境变量
return Logger::DEBUG;//日志级别为DEBUG
else
return Logger::INFO;//否则日志级别为INFO
} Logger::LogLevel g_logLevel = initLogLevel();
//日志级别
const char* LogLevelName[Logger::NUM_LOG_LEVELS] =
{
"TRACE ",
"DEBUG ",
"INFO ",
"WARN ",
"ERROR ",
"FATAL ",
}; // helper class for known string length at compile time
class T
{
public:
T(const char* str, unsigned len)
:str_(str),
len_(len)
{
assert(strlen(str) == len_);
} const char* str_;
const unsigned len_;
}; inline LogStream& operator<<(LogStream& s, T v)
{
s.append(v.str_, v.len_);
return s;
} inline LogStream& operator<<(LogStream& s, const Logger::SourceFile& v)
{
s.append(v.data_, v.size_);
return s;
}
//默认输出
void defaultOutput(const char* msg, int len)
{//输出到标准输出
size_t n = fwrite(msg, 1, len, stdout);
//FIXME check n
(void)n;
}
//默认清空
void defaultFlush()
{//默认清空标准输出
fflush(stdout);
} Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush; } using namespace muduo;
//Impl类的构造函数
//级别,错误(没有错误则传0),文件,行
//Impl类主要是负责日志的格式化
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
: time_(Timestamp::now()),//登记当前的时间
stream_(),//LogStream类
level_(level),//级别
line_(line),//行
basename_(file)//文件名称
{
formatTime();//格式化时间
CurrentThread::tid();//缓存当前线程的id
//输出字符串格式的线程id
stream_ << T(CurrentThread::tidString(), 6);
stream_ << T(LogLevelName[level], 6);
if (savedErrno != 0)//如果savedErrno不为零,还要输出对应的信息
{
stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
}
} void Logger::Impl::formatTime()
{
int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();//获得微秒格式的时间
time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / 1000000);//获得秒
int microseconds = static_cast<int>(microSecondsSinceEpoch % 1000000);//微秒
if (seconds != t_lastSecond)
{
t_lastSecond = seconds;
struct tm tm_time;
::gmtime_r(&seconds, &tm_time); // FIXME TimeZone::fromUtcTime int len = snprintf(t_time, sizeof(t_time), "%4d%02d%02d %02d:%02d:%02d",
tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
assert(len == 17); (void)len;
}
Fmt us(".%06dZ ", microseconds);
assert(us.length() == 9);
//LogStream类重载运算符,把信息输出到缓冲区
stream_ << T(t_time, 17) << T(us.data(), 9);
} void Logger::Impl::finish()
{
stream_ << " - " << basename_ << ':' << line_ << '\n';
}
//构造函数,都是构造Impl对象
Logger::Logger(SourceFile file, int line) : impl_(INFO, 0, file, line)
{
}
//文件名,行,级别,函数名称
Logger::Logger(SourceFile file, int line, LogLevel level, const char* func) : impl_(level, 0, file, line)
{//格式化函数名称
impl_.stream_ << func << ' ';
} Logger::Logger(SourceFile file, int line, LogLevel level): impl_(level, 0, file, line)
{
}
//如果toAbort是真,则级别为FATAL,否则为ERROR
Logger::Logger(SourceFile file, int line, bool toAbort) : impl_(toAbort?FATAL:ERROR, errno, file, line)
{
}
//析构函数
Logger::~Logger()
{
impl_.finish();
//取出stream类缓冲区的内容放到buf
const LogStream::Buffer& buf(stream().buffer());
//输出,g_output有一个默认的输出stdout
g_output(buf.data(), buf.length());
if (impl_.level_ == FATAL)//如果是FATAL
{
g_flush();//清空缓冲区
abort();//终止程序
}
} void Logger::setLogLevel(Logger::LogLevel level)
{
g_logLevel = level;
}
//更改输出函数
void Logger::setOutput(OutputFunc out)
{
g_output = out;
} void Logger::setFlush(FlushFunc flush)
{
g_flush = flush;
}

LogSrtream.h

//日志流
#ifndef MUDUO_BASE_LOGSTREAM_H
#define MUDUO_BASE_LOGSTREAM_H #include <muduo/base/StringPiece.h>
#include <muduo/base/Types.h>
#include <assert.h>
#include <string.h> // memcpy
#ifndef MUDUO_STD_STRING
#include <string>
#endif
#include <boost/noncopyable.hpp> namespace muduo
{ namespace detail
{
//定义两个缓冲区大小的常量
const int kSmallBuffer = 4000;
const int kLargeBuffer = 4000*1000;
//缓冲区类
//模板,但是SIZE是非类型参数
template<int SIZE>
class FixedBuffer : boost::noncopyable//不可被拷贝
{
public:
//构造函数
FixedBuffer() : cur_(data_)//初始化当前指针
{//设置函数指针
setCookie(cookieStart);
}
//析构函数
~FixedBuffer()
{//传递函数指针,这个和构造函数传递的两个函数在下面
setCookie(cookieEnd);
}
//添加数据
void append(const char* /*restrict*/ buf, size_t len)
{
// FIXME: append partially当前可用空间长度大于len
if (implicit_cast<size_t>(avail()) > len)
{//复制数据
memcpy(cur_, buf, len);
//更新当前指针
cur_ += len;
}
//如果当前空间不够用了,这里并没有实现添加部分进来
} const char* data() const { return data_; }
//已经用的长度空间
int length() const { return static_cast<int>(cur_ - data_); }
// write to data_ directly
char* current() { return cur_; }
//当前可用的空间(end减去当前指针)
int avail() const { return static_cast<int>(end() - cur_); }
//增加
void add(size_t len) { cur_ += len; }
//重置空间
void reset() { cur_ = data_; }
//把数据都清零,相当于memset(buf,0,sizeof(buf))
void bzero() { ::bzero(data_, sizeof data_); }
// for used by GDB
//在数据后面加个结束符
const char* debugString();
//将函数指针指向传递进来的函数
void setCookie(void (*cookie)()) { cookie_ = cookie; }
// for used by unit test
//把数据构造一个String对象出来
string asString() const { return string(data_, length()); } private:
//返回的是缓冲区的后一个地址
const char* end() const { return data_ + sizeof data_; }
// Must be outline function for cookies.
static void cookieStart();
static void cookieEnd(); void (*cookie_)();//一个函数指针
char data_[SIZE];//data就是缓冲区,容量为传进来的SIZE
char* cur_;//当前指针
}; }
//LogStream类主要是封装了插入运算符
class LogStream : boost::noncopyable
{
typedef LogStream self;
public:
//包含了LogStream的缓冲区,kSmallBuffer,k开头的表示一个常量,是谷歌的编程规范
typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;
//输出数据首先是输出到缓冲区当中
//以下重载了各种类型的插入运算符
//返回的是自身的引用
self& operator<<(bool v)
{//输出布尔类型
buffer_.append(v ? "1" : "0", 1);//长度是1
return *this;
}
//实现在.cc文件
self& operator<<(short);
self& operator<<(unsigned short);
self& operator<<(int);
self& operator<<(unsigned int);
self& operator<<(long);
self& operator<<(unsigned long);
self& operator<<(long long);
self& operator<<(unsigned long long);
//存放指针
self& operator<<(const void*);
//浮点数
self& operator<<(float v)
{
*this << static_cast<double>(v);
return *this;
}
self& operator<<(double);
// self& operator<<(long double);
//存放单个字符
self& operator<<(char v)
{
buffer_.append(&v, 1);
return *this;
} // self& operator<<(signed char);
// self& operator<<(unsigned char);
//存放字符串
self& operator<<(const char* v)
{//把字符串添加进缓冲区
buffer_.append(v, strlen(v));
return *this;
} self& operator<<(const string& v)
{
buffer_.append(v.c_str(), v.size());
return *this;
}
//如果定义了MUDUO_STD_STRING,则使用的是std::string
//否则使用的是__gnu_cxx::__sso_string
#ifndef MUDUO_STD_STRING
self& operator<<(const std::string& v)
{
buffer_.append(v.c_str(), v.size());
return *this;
}
#endif self& operator<<(const StringPiece& v)
{
buffer_.append(v.data(), v.size());
return *this;
}
//这三个只是改装了以下接口
void append(const char* data, int len) { buffer_.append(data, len); }
const Buffer& buffer() const { return buffer_; }
void resetBuffer() { buffer_.reset(); } private:
void staticCheck();
//成员模板函数
template<typename T>
void formatInteger(T); Buffer buffer_;//大小为kSmallBuffer的缓冲区 static const int kMaxNumericSize = 32;
}; class Fmt // : boost::noncopyable
{
public:
//成员模板,把val进行格式化,然后存放到buf中
template<typename T>
Fmt(const char* fmt, T val); const char* data() const { return buf_; }//返回格式化的数据
int length() const { return length_; }//返回长度 private:
char buf_[32];
int length_;
}; inline LogStream& operator<<(LogStream& s, const Fmt& fmt)
{
s.append(fmt.data(), fmt.length());
return s;
} }
#endif // MUDUO_BASE_LOGSTREAM_H

LogSrtream.cc

#include <muduo/base/LogStream.h>

#include <algorithm>
#include <limits>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h> using namespace muduo;
using namespace muduo::detail; #pragma GCC diagnostic ignored "-Wtype-limits"
//#pragma GCC diagnostic error "-Wtype-limits"
namespace muduo
{
namespace detail
{ const char digits[] = "9876543210123456789";
const char* zero = digits + 9;//digits + 9就是"0"
BOOST_STATIC_ASSERT(sizeof(digits) == 20); const char digitsHex[] = "0123456789ABCDEF";
BOOST_STATIC_ASSERT(sizeof digitsHex == 17); // Efficient Integer to String Conversions, by Matthew Wilson.
//将 一个数转换成字符串
template<typename T>
size_t convert(char buf[], T value)
{
T i = value;
char* p = buf; do
{//对10取模得到最后一位数字
int lsd = static_cast<int>(i % 10);
i /= 10;//更新当前数
//zero将数字转换成字符串,zero指针指向的字符是0,再偏移lsd个位置可以得到相应的字符
*p++ = zero[lsd];
} while (i != 0);
//比如-123会转成"321-"
if (value < 0)//如果要转换的值小于0
{
*p++ = '-';
}
*p = '\0';//添加结束符
std::reverse(buf, p);//使用reverse将字符串进行逆转 return p - buf;//返回转换的字符数
} //uintptr_t类型对于32位平台来说就是unsigned int
//对于64位平台来说就是unsigned long int
size_t convertHex(char buf[], uintptr_t value)
{//转换成16进制的字符,和转换成10进制的差不多
uintptr_t i = value;
char* p = buf;
do
{
int lsd = i % 16;
i /= 16;
*p++ = digitsHex[lsd];
} while (i != 0); *p = '\0';
std::reverse(buf, p); return p - buf;
} }
}
//在数据后面加个\0,相当于字符串
template<int SIZE>
const char* FixedBuffer<SIZE>::debugString()
{
*cur_ = '\0';//加入结束符
return data_;
} template<int SIZE>
void FixedBuffer<SIZE>::cookieStart()
{
} template<int SIZE>
void FixedBuffer<SIZE>::cookieEnd()
{
} template class FixedBuffer<kSmallBuffer>;
template class FixedBuffer<kLargeBuffer>; void LogStream::staticCheck()
{
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<double>::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long double>::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long>::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long long>::digits10);
}
//类型转换成员模板的实现
template<typename T>
void LogStream::formatInteger(T v)
{//当前缓冲区大于32
if (buffer_.avail() >= kMaxNumericSize)//kMaxNumericSize为32
{//把v转换成字符串,存放到buffer_.current()
size_t len = convert(buffer_.current(), v);
//调整buffer_.current()指针
buffer_.add(len);
}
}
//重载各种运算符
LogStream& LogStream::operator<<(short v)
{//short转成int
*this << static_cast<int>(v);
return *this;
} LogStream& LogStream::operator<<(unsigned short v)
{//unsigned short转成unsigned int
*this << static_cast<unsigned int>(v);
return *this;
} LogStream& LogStream::operator<<(int v)
{
formatInteger(v);
return *this;
} LogStream& LogStream::operator<<(unsigned int v)
{
formatInteger(v);
return *this;
} LogStream& LogStream::operator<<(long v)
{
formatInteger(v);
return *this;
} LogStream& LogStream::operator<<(unsigned long v)
{
formatInteger(v);
return *this;
} LogStream& LogStream::operator<<(long long v)
{
formatInteger(v);
return *this;
} LogStream& LogStream::operator<<(unsigned long long v)
{
formatInteger(v);
return *this;
}
//转换指针,是转成16进制的
LogStream& LogStream::operator<<(const void* p)
{//把指针强制转换为uintptr_t类型
//uintptr_t类型对于32位平台来说就是unsigned int
//对于64位平台来说就是unsigned long int
uintptr_t v = reinterpret_cast<uintptr_t>(p);
if (buffer_.avail() >= kMaxNumericSize)
{
char* buf = buffer_.current();//获取当前的位置
buf[0] = '0';//添加两个字符表示是16进制
buf[1] = 'x';
size_t len = convertHex(buf+2, v);
buffer_.add(len+2);//更新当前指针
}
return *this;
} // FIXME: replace this with Grisu3 by Florian Loitsch.
LogStream& LogStream::operator<<(double v)
{
if (buffer_.avail() >= kMaxNumericSize)
{
int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v);
buffer_.add(len);
}
return *this;
}
//把T按fmt格式化后存放到buf中
template<typename T>
Fmt::Fmt(const char* fmt, T val)
{//断言T是算术类型
BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value == true);
//使用snprintf函数进行格式化
length_ = snprintf(buf_, sizeof buf_, fmt, val);
assert(static_cast<size_t>(length_) < sizeof buf_);
} // Explicit instantiations
//模板的特化
template Fmt::Fmt(const char* fmt, char);
template Fmt::Fmt(const char* fmt, short);
template Fmt::Fmt(const char* fmt, unsigned short);
template Fmt::Fmt(const char* fmt, int);
template Fmt::Fmt(const char* fmt, unsigned int);
template Fmt::Fmt(const char* fmt, long);
template Fmt::Fmt(const char* fmt, unsigned long);
template Fmt::Fmt(const char* fmt, long long);
template Fmt::Fmt(const char* fmt, unsigned long long);
template Fmt::Fmt(const char* fmt, float);
template Fmt::Fmt(const char* fmt, double);

编写两个简单的程序使用下日志功能,Log_test1.cc将日志输出到终端,代码如下

//日志输出到标准输出
#include <muduo/base/Logging.h>
#include <errno.h> using namespace muduo; int main()
{ //简单的使用下日志输出
//各种级别的日志输出
LOG_TRACE<<"trace ...";
LOG_DEBUG<<"debug ...";
LOG_INFO<<"info ...";
LOG_WARN<<"warn ...";
LOG_ERROR<<"error ...";
//LOG_FATAL<<"fatal ...";//这里会中断程序,先注释掉
errno = 13;
LOG_SYSERR<<"syserr ...";
//LOG_SYSFATAL<<"sysfatal ...";//这里会中断程序,先注释掉
return 0;
}

运行结果如下所示:



Log_test2.cc将日志输出到文件,代码如下所示:

//日志输出到文件
#include <muduo/base/Logging.h>
#include <errno.h>
#include <stdio.h> using namespace muduo; FILE* g_file;//定义一个文件
//muduo中的 typedef void (*OutputFunc)(const char* msg, int len);
void dummyOutput(const char* msg, int len)
{
if (g_file)
{//将内容写到文件里
fwrite(msg, 1, len, g_file);
}
} void dummyFlush()
{
fflush(g_file);//清空输出
} int main()
{
g_file = ::fopen("/mnt/hgfs/lcw_program/lcw_muduo_learning/tests/muduo_log", "ae");//打开文件
Logger::setOutput(dummyOutput);//设置输出到文件,dummyOutput为函数
Logger::setFlush(dummyFlush); LOG_TRACE<<"trace ...";
LOG_DEBUG<<"debug ...";
LOG_INFO<<"info ...";
LOG_WARN<<"warn ...";
LOG_ERROR<<"error ...";
//LOG_FATAL<<"fatal ...";//这里会中断程序,先注释掉
errno = 13;
LOG_SYSERR<<"syserr ...";
//LOG_SYSFATAL<<"sysfatal ..."; ::fclose(g_file);//关闭文件 return 0;
}

运行结果如下:



文件内容如下:

muduo网络库源码学习————日志类封装的更多相关文章

  1. muduo网络库源码学习————线程类

    muduo库里面的线程类是使用基于对象的编程思想,源码目录为muduo/base,如下所示: 线程类头文件: // Use of this source code is governed by a B ...

  2. muduo网络库源码学习————日志滚动

    muduo库里面的实现日志滚动有两种条件,一种是日志文件大小达到预设值,另一种是时间到达超过当天.滚动日志类的文件是LogFile.cc ,LogFile.h 代码如下: LogFile.cc #in ...

  3. muduo网络库源码学习————Exception类

    Exception类是为异常捕获而设计,可以获得异常的信息以及栈的回溯信息 (原来的代码没有demangle成员函数,输出的格式比较难看,加了demangle成员函数,利用demangle成员函数可以 ...

  4. muduo网络库源码学习————Timestamp.cc

    今天开始学习陈硕先生的muduo网络库,moduo网络库得到很多好评,陈硕先生自己也说核心代码不超过5000行,所以我觉得有必要拿过来好好学习下,学习的时候在源码上面添加一些自己的注释,方便日后理解, ...

  5. muduo网络库源码学习————线程本地单例类封装

    muduo库中线程本地单例类封装代码是ThreadLocalSingleton.h 如下所示: //线程本地单例类封装 // Use of this source code is governed b ...

  6. muduo网络库源码学习————线程池实现

    muduo库里面的线程池是固定线程池,即创建的线程池里面的线程个数是一定的,不是动态的.线程池里面一般要包含线程队列还有任务队列,外部程序将任务存放到线程池的任务队列中,线程池中的线程队列执行任务,也 ...

  7. muduo网络库源码学习————互斥锁

    muduo源码的互斥锁源码位于muduo/base,Mutex.h,进行了两个类的封装,在实际的使用中更常使用MutexLockGuard类,因为该类可以在析构函数中自动解锁,避免了某些情况忘记解锁. ...

  8. muduo网络库源码学习————线程特定数据

    muduo库线程特定数据源码文件为ThreadLocal.h //线程本地存储 // Use of this source code is governed by a BSD-style licens ...

  9. muduo网络库源码学习————无界队列和有界队列

    muduo库里实现了两个队列模板类:无界队列为BlockingQueue.h,有界队列为BoundedBlockingQueue.h,两个测试程序实现了生产者和消费者模型.(这里以无界队列为例,有界队 ...

随机推荐

  1. Linux服务器 上传/下载 文档/目录

    1.从服务器上下载文件 scp username@servername:/path/filename /var/www/local_dir(本地目录) 例如scp root@192.168.0.101 ...

  2. Array(数组)对象-->unshift() 方法

    1.定义和用法 unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度. 语法: array.unshift(item1,item2, ..., itemX) 参数:item1,it ...

  3. Win安装docker

    Windows Docker 安装 win7.win8 系统 win7.win8 等需要利用 docker toolbox 来安装,国内可以使用阿里云的镜像来下载,下载地址:http://mirror ...

  4. shell命令-if语句

    判断参数的个数 -ne 不等于 -eq 等于 -gt 大于 -lt 小于 -ge 大于等于 -le 小于等于 if [ "$#" -ne 1 ];then echo "n ...

  5. Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(二)之Introduction to Objects

    The genesis of the computer revolution was a machine. The genesis of out programming languages thus ...

  6. E - Max Sum Plus Plus Plus HDU - 1244 (线性区间DP)

    题目大意:  值得注意的一点是题目要求的是这些子段之间的最大整数和.注意和Max Sum Plus Plus这个题目的区别. 题解: 线性区间DP,对每一段考虑取或者不取.定义状态dp[i][j]指的 ...

  7. 架构师修炼之微服务部署 - Docker简介

    Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化,容器是 ...

  8. css特效sh

    1   opacity=0.5:                                                             透明度 2    选择器   .btn1:ho ...

  9. 泛微ecology OA系统在数据库配置信息泄露

    漏洞描述 攻击者可通过该漏洞页面直接获取到数据库配置信息,攻击者可通过访问存在漏洞的页面并解密从而获取数据库配置信息,如攻击者可直接访问数据库,则可直接获取用户数据,由于泛微e-cology默认数据库 ...

  10. Java中常量的概念

    常量:在程序执行过程中,其值不发生改变的量.分类:A:字面值常量B:自定义常量字面值常量A:字符串常量(用“”括起来的内容).举例:"hello"B:整数常量 (所有的整数)举例: ...