muduo网络库base源码分析2

1 线程安全的单例模板类实现

在这里插入图片描述

静态 Public 成员函数
static T &instance () 用于返回单例对象
静态 Private 成员函数
static voidinit () 用于创建单例对象
static voiddestroy () 用于销毁单例对象
静态 Private 属性
static pthread_once_tponce_ = PTHREAD_ONCE_INIT 能够保证一个函数只会被调用一次
static T *value_ = NULL

该类的文档由以下文件生成:

 static T& instance()
        {
            pthread_once(&ponce_, &Singleton::init); // 保证只被调用一次并且可以保证线程安全,效率比锁更高
            return *value_;
        }
static void init()
        {
            value_ = new T();
            ::atexit(destroy);
        }

atexit注册一个函数,等程序结束后自动调用

由于是个单例模板类型,我们不知道究竟销毁的类型。而且单例已经私有化了他的析构函数,就不能再用智能指针去管理了,会很麻烦。

比如

class A;
A *pa ;
delete pa;
// 不会报错,只会警告的前向声明,他就是一个不完全的类型
static void destroy()
        {
            typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; // 尽可能让他释放的时候,在编译阶段
    // 因为sizeof是测了不完全类型,所以在sizeof的时候,就会报错 boost库很多这种写法
            T_must_be_complete_type dummy; (void) dummy;
            delete value_;
        }
2 线程特定全局数据 TSD

pthread_key_create可以指定回调函数销毁数据

pthread_key_delete删除key,不删除数据

pthread_getspecific获取key的数据,返回值是void *类型

pthread_setspecific 指定特定的数据key,令线程私有,但可以跨函数使用

muduo::ThreadLocal< T > 模板类 参考

类 muduo::ThreadLocal< T > 继承关系图:

[图例]

muduo::ThreadLocal< T > 的协作图:
在这里插入图片描述

[图例]

Public 成员函数
T &value ()
静态 Private 成员函数
static voiddestructor (void *x)
Private 属性
pthread_key_tpkey_ 线程特定数据

该类的文档由以下文件生成:

1 示例代码
#include <cstdio>
#include <cstring>

#include <pthread.h>
pthread_key_t key;
void func()
{
    int *temp = static_cast<int *>(pthread_getspecific(key)); // 返回值是void* 需要强制转换
    printf(" func pid = %d temp = %d  %s\n",pthread_self(),*temp,__func__);

}
void* thread_func(void *arg)
{
    pthread_setspecific(key,arg);
    int* temp = static_cast<int *>(pthread_getspecific(key));
    // 获取该线程的私有空间
    printf("temp = %d pid = %d  %s\n",*temp,pthread_self(),__func__);

    *temp = (*temp)*1000;
    func();
    return NULL;
}
int main()
{
    pthread_t t1,t2;
    int a = 1,b = 2;
    pthread_key_create(&key,NULL);
    pthread_create(&t1,NULL,thread_func,&a);
    pthread_create(&t2,NULL,thread_func,&b);
    // 分别创建两个线程来测试各自的线程的全局变量 
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    
}
g++ test.cc -lpthread 
./a.out 
temp = 2 pid = -1437845760  thread_func
 func pid = -1437845760 temp = 2000  func
temp = 1 pid = -1429453056  thread_func
 func pid = -1429453056 temp = 1000  func

ThreadLocal()
        {
            pthread_key_create(&pkey_, &ThreadLocal::destructor); // 创建key和指定销毁的函数
        }

        ~ThreadLocal()
        {
            pthread_key_delete(pkey_);	// 只删除key,数据由构造函数注册的去销毁
        }

根据线程的key来获取,数据的引用

T& value()
        {
            T* perThreadValue = static_cast<T*>(pthread_getspecific(pkey_)); // 获取指定key对应的数据,并且转为T*
            if (!perThreadValue) { 	 // 如果线程key对应的数据是空的,那就说明还没创建这个数据
                T* newObj = new T(); // 重新创建数据,并且设置
                pthread_setspecific(pkey_, newObj);
                perThreadValue = newObj; 
            }
            return *perThreadValue;
        }
2 示例代码
#include <muduo/base/ThreadLocal.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Thread.h>

#include <boost/noncopyable.hpp>
#include <stdio.h>

class Test : boost::noncopyable
{
public:
	Test()
	{
		printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
	}

	~Test()
	{
		printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
	}

	const std::string &name() const { return name_; }
	void setName(const std::string &n) { name_ = n; }

private:
	std::string name_;
};

muduo::ThreadLocal<Test> testObj1;
muduo::ThreadLocal<Test> testObj2; // 定义了两个线程特定数据对象单例类,也就是每个线程的key都对应了一个test对象

void print()
{
	printf("tid=%d, obj1 %p name=%s\n",
		   muduo::CurrentThread::tid(),
		   &testObj1.value(),
		   testObj1.value().name().c_str());
	printf("tid=%d, obj2 %p name=%s\n",
		   muduo::CurrentThread::tid(),
		   &testObj2.value(),
		   testObj2.value().name().c_str());
}

void threadFunc()
{
	print();
	testObj1.value().setName("changed 1");
	testObj2.value().setName("changed 42");
	print(); 
}

int main()
{
	testObj1.value().setName("main one"); // 获取线程特定数据,并且设置名字
	print();  // 把tid和Test对象的地址,test对象名打印出来,test2根本没有设置Name所以打印出来的是空的
	muduo::Thread t1(threadFunc);
	t1.start();
	t1.join();
	testObj2.value().setName("main two");
	print();

	pthread_exit(0);
}
3 日志类

在这里插入图片描述

muduo::Logger类 参考

muduo::Logger 的协作图:

  • Impl格式化日志,都是由该类实际实现的,借助LogStream来输出日志
  • LogStream类实际上也并没有立刻输入到文件,而是先放到缓冲区,重载了<<运算符
  • FixedBuffer缓冲区,是否输出到文件或者终端。等待flush的时机。
classImpl
classSourceFile
Public 类型
enumLogLevel { TRACE, DEBUG, INFO, WARN, ERROR, FATAL, NUM_LOG_LEVELS }
typedef void(*OutputFunc) (const char *msg, int len)
typedef void(*FlushFunc) ()
Public 成员函数
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)
LogStream &stream ()
静态 Public 成员函数
static LogLevellogLevel ()
static voidsetLogLevel (LogLevel level)
static voidsetOutput (OutputFunc)
static voidsetFlush (FlushFunc)
Private 属性
Implimpl_

该类的文档由以下文件生成:

1 日志类的宏定义
#define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \
  muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()
  // 如果日志级别大于TRACE 就不会构造TRACE
#define LOG_DEBUG if (muduo::Logger::logLevel() <= muduo::Logger::DEBUG) \
  muduo::Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream()
  // 如果日志级别大于= DEBUG 就不会构造
// ……略

构造Logger匿名对象,并且把文件名,行号,和日志级别,以及函数名传递进去

Logger::Logger(SourceFile file, int line, LogLevel level, const char* func)
        : impl_(level, 0, file, line) // 调用impl构造函数
{
    impl_.stream_ << func << ' '; // 格式化函数名称
}
// impl是Logger的内嵌类

impl的构造函数会记录时间,流,等级和行号等

Public 成员函数
Impl (LogLevel level, int old_errno, const SourceFile &file, int line)
voidformatTime () 用于格式化时间
voidfinish () 用于刷新缓冲区
Public 属性
Timestamptime_
LogStreamstream_
LogLevellevel_
intline_
SourceFilebasename_
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
        : time_(Timestamp::now()),
          stream_(),
          level_(level),
          line_(line),
          basename_(file)
{
    formatTime();
    CurrentThread::tid(); // 获取当前线程id的缓存
    stream_ << T(CurrentThread::tidString(), 6); // 暂存线程名称
    stream_ << T(LogLevelName[level], 6); // 暂存日志等级到缓冲区
    if (savedErrno != 0) // 如果有了错误,就会输出
    {
        stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
    }
}

20220105 01:48:22.854411Z 13505 TRACE main trace … - Log_test1.cc:8

格式化时间 线程ID 日志等级 函数名+行号

20220105 01:48:22.855115Z 13505 DEBUG main debug … - Log_test1.cc:9
20220105 01:48:22.855600Z 13505 INFO info … - Log_test1.cc:10
20220105 01:48:22.856487Z 13505 WARN warn … - Log_test1.cc:11
20220105 01:48:22.858032Z 13505 ERROR error … - Log_test1.cc:12
20220105 01:48:22.858525Z 13505 ERROR Permission denied (errno=13) syserr … - Log_test1.cc:15
20220105 01:48:22.859391Z 13505 FATAL Permission denied (errno=13) sysfatal … - Log_test1.cc:16

2 缓冲区详解

g_output输出路径

Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush;
void defaultOutput(const char* msg, int len)
    {
        size_t n = fwrite(msg, 1, len, stdout);
        //FIXME check n
        (void)n;
    }
 void defaultFlush()
    {
        fflush(stdout);
    }

默认是指定到stdout标准输出,默认是刷新标准输出,可以通过更改输出函数,来改变输出位置

Logger::~Logger()
{
    impl_.finish();
    const LogStream::Buffer& buf(stream().buffer()); // 用的& buf 接收过来,并没有拷贝
    g_output(buf.data(), buf.length());
    if (impl_.level_ == FATAL)
    {
        g_flush();
        abort();
    }
}
3 示例代码
FILE *g_file;

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("/chen/cpp/1.log", "ae");
	Logger::setOutput(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;
}
4 LogStream输出流类

在这里插入图片描述

Public 类型
typedef detail::FixedBuffer< detail::kSmallBuffer >Buffer
Public 成员函数
self &operator<< (bool v)
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)
self &operator<< (double)
self &operator<< (char v)
self &operator<< (const char *v)
self &operator<< (const string &v)
self &operator<< (const std::string &v)
self &operator<< (const StringPiece &v)
voidappend (const char *data, int len)
const Buffer &buffer () const
voidresetBuffer ()
Private 类型
typedef LogStreamself
Private 成员函数
voidstaticCheck ()
template
voidformatInteger (T)
Private 属性
Bufferbuffer_
静态 Private 属性
static const intkMaxNumericSize = 32

格式化bool类型

self& operator<<(bool v)
        {
            buffer_.append(v ? "1" : "0", 1);
            return *this;
        }

LogStream& LogStream::operator<<(short v)
{
    *this << static_cast<int>(v); // 把short转为int再格式化
    // 基本上涉及到整数的long unsigned long short都被转为int类型了
    return *this;
}
5 FixedBuffer 缓冲区类
 template<int SIZE> // SIZE为非类型参数,通过模板传递创建缓冲区大小
        class FixedBuffer : boost::noncopyable
        {// ……略};
Public 成员函数
voidappend (const char *buf, size_t len) 往缓冲区追加数据
const char *data () const
intlength () const 当前有效长度
char *current () 返回当前可用元素的位置
intavail () const
voidadd (size_t len)
voidreset () 空闲重置,重复利用
voidbzero () 清空为0,相当于memset
const char *debugString ()
voidsetCookie (void(*cookie)())
stringasString () const
Private 成员函数
const char *end () const 指向最后一个元素的末尾
静态 Private 成员函数
static voidcookieStart ()
static voidcookieEnd ()
Private 属性
void(*cookie_ )()
chardata_ [SIZE] 存放缓冲区的数据
char *cur_ 指向当前可有元素的位置

end()-cur_ =当前可有的元素

cur_ - data =length =当前有效长度

void append(const char* buf, size_t len)
            {
                if (implicit_cast<size_t>(avail()) > len) // implicit_cast 重新解释
                {
                    memcpy(cur_, buf, len);  // 如果当前可用空间大于len,就把数据拷贝过来
                    cur_ += len;
                }
            }
// 把数值类型,转换成字符串存储放到缓冲区中
void LogStream::formatInteger(T v)
{
    if (buffer_.avail() >= kMaxNumericSize) // 缓冲区可用空间必须大于= 32位数,不然放不下
    {
        size_t len = convert(buffer_.current(), v);
        buffer_.add(len);
    }
}
const char digits[] = "9876543210123456789";
        const char* zero = digits + 9;
template<typename T>
        size_t convert(char buf[], T value)
        {
            T i = value;
            char* p = buf;

            do
            {
                int lsd = static_cast<int>(i % 10); // 最后一个数字取余10 ,这样子不停地取出最后一位数 123 321 出来
                i /= 10;
                *p++ = zero[lsd]; // 从0 偏移到3右边3的位置
           
               
            } while (i != 0);

            if (value < 0) // 如果是负数
            {
                *p++ = '-';
            }
            *p = '\0';
            std::reverse(buf, p); // 反转字符串

            return p - buf; // 求转换了多少个字符,比如1234 转了4个
        }

会把指针转换成16进制的地址存放进去

6 其它 STL

提供通用的操作,而又不损失效率,通过定义一些结构体和类,利用模板特化给类型赋予特性

#ifdef HAVE_TYPE_TRAITS
// This makes vector<StringPiece> really fast for some STL implementations
template<> struct __type_traits<muduo::StringPiece> {
  typedef __true_type    has_trivial_default_constructor;
  typedef __true_type    has_trivial_copy_constructor;
  typedef __true_type    has_trivial_assignment_operator;
  typedef __true_type    has_trivial_destructor;
  typedef __true_type    is_POD_type;
};

说通俗点就是,int类型可以用=赋值,无所谓,哪如果是struct和class呢,我需要在vector的时候对它进行拷贝,就会用到memset了,所以需要用到类型萃取。

struct TestType
{
    bool Get()
    {
        return false;
    }
};

,利用模板特化给类型赋予特性

#ifdef HAVE_TYPE_TRAITS
// This makes vector<StringPiece> really fast for some STL implementations
template<> struct __type_traits<muduo::StringPiece> {
  typedef __true_type    has_trivial_default_constructor;
  typedef __true_type    has_trivial_copy_constructor;
  typedef __true_type    has_trivial_assignment_operator;
  typedef __true_type    has_trivial_destructor;
  typedef __true_type    is_POD_type;
};

说通俗点就是,int类型可以用=赋值,无所谓,哪如果是struct和class呢,我需要在vector的时候对它进行拷贝,就会用到memset了,所以需要用到类型萃取。

struct TestType
{
    bool Get()
    {
        return false;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值