mudo基础库

为什么需要网络库

使用Sockets API进行网络编程是一件简单地计数,但是高级语言的Sockets库并没有对Sockets API提供更高层次的封装,直接用它编写网络程序很容易掉到陷阱里,因此需要网络库来降低开发难度,网络库的价值还在于能方便地处理并发连接。

library和framework的区别

1.库是将代码集合成的一个产品,供程序员调用。面向对象的代码组织形式而成的库也叫类库。面向过程的代码组织形式而成的库也叫函数库。

在函数库中的可直接使用的函数叫库函数。开发者在使用库的时候,只需要使用库的一部分类或函数,然后继续实现自己的功能。

框架则是为解决一个(一类)问题而开发的产品,框架用户一般只需要使用框架提供的类或函数,即可实现全部功能。可以说,框架是库的升级版。开发者在使用框架的时候,必须使用这个框架的全部代码。

2.库中的类相对比较独立,我们编写应用的时候需要编写一些“胶水代码”来粘合。

框架是恶能够应用于特定领域的,不需要编写过多的“胶水代码”

3.框架与库的重要区别是:框架提供用户一些回调函数,使得框架能够调用我们所编写的回调函数,这就使得控制反转了

muduo库可以认为是一个小型框架。

Reactor模式

muduo库中有许多Event Loop(如果是单线程的话只有一个Event Loop),这个Event Loop实际上就是poll、epoll机制,是一个循环,在等待网络事件,如果网络事件触发,就回调我们的应用。

比如当一个客户端连接的时候,能够回调表示客户端已连接,那么客户端连接以后的一些具体操作,就可以在App中编写逻辑,当有数据到来的时候,muduo库能够回调有消息到来的对应函数,使得具体的业务逻辑可以在应用层进行编写,而不是把具体的业务放在库中编写。


面向对象的编程风格

muduo库只暴露具体类,不暴露抽象类,也不适用虚函数做接口

基于对象的编程风格

使用boost::function、boost::bind实现基于对象的编程风格

基础库

muduo/base目录是一些基础库,都是用户可见的类,可以在用户程序中直接使用。

base

AsyncLogging.(h,cc)                                       异步日子backend

Atomic.h                                                       原子操作与原子整数

BlockingQueue.h                                          无界阻塞队列(消费者生产者队列)

BoundedBlockingQueue.h                            有界阻塞队列

Condition.h                                                  条件变量,与Mutex一同使用

copyable.h                                                    一个空基类,用于标识(tag)值类型

CountDownLatch                                         "倒计时门闩"同步

Date.{h,cc}                                                    Julian日期库(即公历)

Exception.{h,cc}                                             带stack trace的异常基类

Logging.{h,cc}                                               简单的日志,可搭配AsynLogging使用

Mutex.h                                                        互斥器

ProcessInfo.{h,cc}                                         进程信息

Singleton.h                                                  线程安全的singleton

StringPiece.h                                                从Google开源代码借用的字符串参数传递类型

tests                                                            测试代码

Thread.{h,cc}                                                线程对象

ThreadLocal.h                                              线程局部数据

ThreadLocalSingleton.h                               每个线程一个singleton

ThreadPool.{h,cc}                                         简单的固定大小线程池

Timestamp.{h,cc}                                         UTC时间戳

TimeZone.{h,cc}                                           时区与夏令时

Types.h                                                        基本类型的声明,包括muduo::string


1.Timestamp.h 

时间戳,关于时间的类

2.Types.h   

提供两个转型函数

3.Atomic.h   

原子操作与原子整数操作的封装类

4.Exception.{h,cc}   

异常机制,通过异常机制我们可以轻松的捕获要出现的异常

5.Thread.{h,cc} 

线程类的实现,采用的是基于对象的方式实现的。启动线程(创造线程)->线程的入口函数->runInThread()->回调函数(通过构造函数引入的)

6.一些IPC组件的封装

MutexLock/MutexLockGuard    

互斥锁

MutexLock有死锁的风险,比如:

MutexLock mutex;
void f()
{
    mutex.lock();
    if(...)
    {
        ...
        return;//从这里退出的话没有解锁,所以存在死锁的风险
    }
    mutex.unlock();
}

使用MutexLockGuard 的话,会在构造函数中加锁,析构函数中解锁,所以更加保险

也就是说,return的时候,函数销毁,自动调用析构函数,从而解锁

Condition.{h,cc} 

条件变量

CountDownLatch.{h,cc}  

实际上是对条件变量Condition类的封装,倒计时门闩类。既可以用于子线程等待主线程发起“起

跑”,也可以用于主线程等待子线程初始化完毕才开始工作

7.两个队列的实现,其实就是muduo库中生产者、消费者线程的实现

BlockingQueue<T>  

无界缓冲区,借助于queue

BoundedBlockingQueue<T>  

有界缓冲区,借助于助于boost中的环形缓冲区

生产者-消费者模型既可以用信号量来实现,也可以用条件变量,上面两者用的都是条件变量。

8.Thread.{h,cc} 

线程池的实现,这个线程池是固定线程池,线程的个数是固定的,不能自动伸缩。线程池问题本质上也是一个生产者、消费者问题。

线程池中有一个线程队列还有一个任务队列,外部线程可以往线程池中的任务队列添加任务,这些外部线程相当于生产者;一旦任务队列中有任务就唤醒线程队列中的线程来执行这些任务,这些线程相当于消费者线程。

9.Singleton.h   

线程安全的单例实现,保证一个类只有一个实例,单例对象

pthread_once能够保证某函数只被执行一次,这样的话能够保证对象只被生成一个并且是线程安全的

10.ThreadLocal.h线程本地存储

在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。
在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。 
但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。
POSIX线程库通过维护一定的数据结构来解决这个问题,这个些数据称为(Thread-specific Data,或 TSD)。
线程特定数据也称为线程本地存储TLS(Thread-local storage)

对于POD类型的线程本地存储,可以用__thread关键字

POSIX线程库通过4个函数来操作线程特定数据

pthread_key_create

创建一个key,一旦一个线程创建了一个key,所有线程都有这个key,不同的线程都可以通过相同的key访问实际数据,但数据是各不相同的


pthread_key_delete  删除key
pthread_getspecific  获取特定数据
pthread_setspecific  指定特定数据

muduo中的ThreadLocal<T>就是对上面4个函数的封装

11.ThreadLocalSigleton.h

 线程本地单例封装,每个线程都有T类型的单例对象,前面的Singleton是所有线程共有的单例对象

12.Logging.{h,cc}  

日志类的封装

TRACE
指出比DEBUG粒度更细的一些信息事件(开发过程中使用)
DEBUG
指出细粒度信息事件对调试应用程序是非常有帮助的。(开发过程中使用)
INFO
表明消息在粗粒度级别上突出强调应用程序的运行过程。
WARN
系统能正常运行,但可能会出现潜在错误的情形。
ERROR
指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL
指出每个严重的错误事件将会导致应用程序的退出。

muduo库的日志输出很简单,靠的是几个宏,既可以输出到标准输出,也可以输出到文件

整个过程是首先构造一个Logger对象,然后调用stream()方法,返回一个LogStream对象,再通过调用这个对象的operator<<()进而输出日志到标准输出或指定文件,如下:


13. LogStream.{h,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);

StringPiece.h  可以把这个类当成是字符串,用以实现高效的字符串传递

const char* s1;
std::string s2;
void foo(const StringPiece& x);
这里既可以用const char*、也可以用std::string类作为参数传递,并且不涉及内存拷贝
如果采用void foo(const std::string& x);
对于前面两者也都可以实现

但对于const char* str_就涉及内存拷贝,效率不高

14. LogFile.{h,cc}  

日志滚动

日志滚动条件
文件大小(例如每写满1G换下一个文件)

时间(每天零点新建一个日志文件,不论前一个文件是否写满)日志滚动条件

上面两个条件任意一个满足,都会产生一个新的文件

一个典型的日志文件名
logfile_test.20130411-115604.popo.7743.log

日志文件的basename.年.月.日.时.分.秒.主机名称.进程ID.后缀

FileUtil.{h,cc}中的class AppendFile,文件类的实现;class ReadSmallFile,小文件类的实现

猜你喜欢

转载自blog.csdn.net/wk_bjut_edu_cn/article/details/80827349
今日推荐