sylar服务器框架:线程与协程模块
线程模块
模块概述
提供线程类和线程同步类,基于pthread实现。
模块分析
Thread
:线程类,通过构造函数传入线程入口函数和线程名称,线程构造之后线程开始运行。
Semaphore
:计数信号量,基于sem_t实现。
Mutex
: 互斥锁,基于pthread_mutex_t
实现。
RWMutex
: 读写锁,基于pthread_rwlock_t
实现。
Spinlock
: 自旋锁,基于pthread_spinlock_t
实现。
CASLock
: 原子锁,基于std::atomic_flag
实现。
协程模块
模块概述
基于ucontext_t实现非对称协程。协程也被称为用户态轻量级线程。每个协程在创建的时候都会指定一个入口函数,保存其上下文。协程的本质就是函数和函数运行状态的组合。
协程和函数的不同之处是,函数一旦被调用,只能从头开始执行,直到函数执行结束退出,而协程则可以执行到一半就暂时让出CPU执行权(称为yield),在后面的适当的时机协程可以重新恢复运行(称为resume),在这段时间内其他的协程可以获得CPU并运行,所以协程也称为轻量级线程。
模块分析
sylar的协程模块基于ucontext_t实现,关于ucontext_t的定义和相关的接口如下:
typedef struct ucontext_t {
// 当前上下文结束后,下一个激活的上下文对象的指针,只在当前上下文是由makecontext创建时有效
struct ucontext_t *uc_link;
// 当前上下文的信号屏蔽掩码
sigset_t uc_sigmask;
// 当前上下文使用的栈内存空间,只在当前上下文是由makecontext创建时有效
stack_t uc_stack;
// 平台相关的上下文具体内容,包含寄存器的值
mcontext_t uc_mcontext;
...
} ucontext_t;
// 获取当前的上下文
int getcontext(ucontext_t *ucp);
// 恢复ucp指向的上下文,这个函数不会返回,而是会跳转到ucp上下文对应的函数中执行,相当于变相调用了函数
int setcontext(const ucontext_t *ucp);
// 修改由getcontext获取到的上下文指针ucp,将其与一个函数func进行绑定,支持指定func运行时的参数,
// 在调用makecontext之前,必须手动给ucp分配一段内存空间,存储在ucp->uc_stack中,这段内存空间将作为func函数运行时的栈空间,
// 同时也可以指定ucp->uc_link,表示函数运行结束后恢复uc_link指向的上下文,
// 如果不赋值uc_link,那func函数结束时必须调用setcontext或swapcontext以重新指定一个有效的上下文,否则程序就跑飞了
// makecontext执行完后,ucp就与函数func绑定了,调用setcontext或swapcontext激活ucp时,func就会被运行
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
// 恢复ucp指向的上下文,同时将当前的上下文存储到oucp中,
// 和setcontext一样,swapcontext也不会返回,而是会跳转到ucp上下文对应的函数中执行,相当于调用了函数
// swapcontext是sylar非对称协程实现的关键,线程主协程和子协程用这个接口进行上下文切换
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp);
非对称协程模型
:
sylar框架使用非对称协程模型,子协程只能与主协程切换,不能在子协程间切换。
协程状态
:
enum State {
INIT, /// 初始化状态
HOLD, /// 暂停状态
EXEC, /// 执行中状态
TERM, /// 结束状态
READY, /// 可执行状态
EXCEPT /// 异常状态
};
协程原语
:
对于非对称协程来说,协程除了创建语句外,只有两种操作,一种是resume,表示恢复协程运行,一种是yield,表示让出执行。协程的结束没有专门的操作,协程函数运行结束时协程即结束,协程结束时会自动调用一次yield以返回主协程。
//切换到当前协程执行 ---resume
void Fiber::swapIn() {
SetThis(this);
SYLAR_ASSERT(m_state != EXEC);
m_state = EXEC;
if(swapcontext(&Scheduler::GetMainFiber()->m_ctx, &m_ctx)) {
SYLAR_ASSERT2(false, "swapcontext");
}
}
//切换到后台执行 ---yield
void Fiber::swapOut() {
SetThis(Scheduler::GetMainFiber());
if(swapcontext(&m_ctx, &Scheduler::GetMainFiber()->m_ctx)) {
SYLAR_ASSERT2(false, "swapcontext");
}
}