preface
#ifndef __MANAGE_HPP__
#define __MANAGE_HPP__
#include "log.hpp"
#include "commonlib.hpp"
class Empty {
};
template<class thd, class DATA = Empty>
class Manage {
public:
//virtual int crea(thd *&t, DATA *para = NULL, string str = "");
//run thread
static int attachthread(thd &t);
//stop thread
static int stop(thd &t);
//del thread object
static int del(thd *t);
};
#include "manage.inl"
#endif
//log类静态成员对象完成日志级别、文件等设置,
//log是类名, logt指向单例log类静态成员,代表一个指针
if(0 != Manage<log>::attachthread(*logt()))
{
//运行日志thread失败
record(__FILE__, __LINE__, errlevel, "failed.");
return -1;
}
//现在可以使用log2file把日志写入日志文件了
log2file(__FILE__, __LINE__, level, "log ok.");
一、
二、
三、
从两个角度来表述
1、为什么要创建新线程?
日志系统需要单独一个线程,核心原因是异步写日志。
如果没有独立线程(同步写日志)
业务线程
↓
调用 log2file()
↓
直接写磁盘 ← 磁盘 I/O 很慢,可能几毫秒到几十毫秒
↓
写完才能继续业务逻辑 ← 业务被阻塞了!
有了独立日志线程(异步写日志)
业务线程 日志线程(独立运行)
↓ ↓
调用 log2file() 不断从队列取日志
↓ ↓
把日志消息塞进队列 写入磁盘文件
↓(立刻返回,不等待)
继续执行业务逻辑
好处很明显:
- 业务线程不被 I/O 阻塞,性能更好
- 日志线程专职写盘,有序、不竞争
- 多个业务线程都可以同时往队列里丢日志,日志线程统一消费
2、*logt() 传进 attachthread() 有什么用?
先看调用:
Manage<log>::attachthread(*logt());
// ↑
// 解引用,得到 log类的 对象的引用
attachthread() 的签名是:
static int attachthread(thd &t); // 接收的是引用
所以 *logt() 传进去之后,在 attachthread() 内部就是 t,它的作用贯穿整个函数:
// 1. 用它来同步(父子线程握手)
t.lock();
t.wait_cond();
t.sig_cond();
t.unlock();
// 2. 把它的指针传给新线程作为参数
thd *pt = dynamic_cast<thd*>(&t);
pthread_create(&tid, &a, start_routine, (void *)pt);
// ↑
// 新线程拿到这个指针后
// 就能操作 log类的 对象本身
关键点:新线程拿到的是同一个对象
logt() 返回 log类 的单例指针
*logt() 解引用为对象引用,传入 attachthread()
&t → pt 在 attachthread() 内部取回指针
pthread_create 把 pt 传给新线程的入口函数 newthreadname
所以新线程启动后,拿到的 pt 和 logt() 指向的是同一个 log类的 对象,可以直接调用它的方法(读队列、写日志、更新状态等)。
整体关系图
主线程
logt() ──────────────────────────┐
↓ │ 同一个对象
*logt() 传入 attachthread() │
↓ │
pthread_create(pt) ─────→ 日志线程
│
start_routine(pt)
↓
pt->attachthread() // log类 的业务循环
从队列取日志,写文件...
简单说:*logt() 传进去,就是告诉 Manage——“帮我把这个日志对象跑在一个新线程里”。

695

被折叠的 条评论
为什么被折叠?



