skynet是一个轻量级的游戏服务器框架;实现了actor的并发模型;可以基于skynet框架去实现业务。
多核并发编程
多线程
在一个进程中开启多线程,为了充分利用多核,一般设置工作线程的个数为 cpu 的核心数;
memcached 就是采用这种方式;
多线程在一个进程当中,所以数据共享来自进程当中的内存;这里会涉及到很多临界资源的访问,所以需要考虑加锁;
多进程
在一台机器当中,开启多个进程充分利用多核,一般设置工作进程的个数为 cpu 的核心数;
nginx 就是采用这种方式;
CSP
以 go 语言为代表,并发实体是协程(用户态线程、轻量级线程);内部也是采用多少个核心开启多少个内核线程来充分利用多核;
actor
erlang 从语言层面支持 actor 并发模型,并发实体是 actor(用户态进程);skynet采用 c + lua来实现 actor 并发模型, skynet中的actor也叫服务;底层也是通过采用多少个核心开启多少个内核线程来充分利用多核;
总结
不要通过共享内存来通信,而应该通过通信来共享内存。
actor定义
为什么要抽象进程?
每一个进程就是一个运行实体,这些运行实体提供了相互隔离的运行环境。
// 服务的定义,就是actor
struct skynet_context {
void * instance; // 隔离的运行环境,一块内存或者lua虚拟机
struct skynet_module * mod; // 服务的启动文件
void * cb_ud;
skynet_cb cb; // callback,通过调用cb来运行actor
struct message_queue *queue; // 消息队列,按照消息到达的先后顺序执行它
ATOM_POINTER logfile;
uint64_t cpu_cost; // in microsec
uint64_t cpu_start; // in microsec
char result[32];
uint32_t handle; // actor的id
int session_id;
ATOM_INT ref; // 引用计数
int message_count; // actor处理了多少个消息
bool init; // 是否初始化
bool endless; // 是否陷入死循环
bool profile; // 是否调试状态
CHECKCALLING_DECL
};
actor组成部分
-
隔离环境,主要是lua虚拟机,也有可能是一块内存
-
回调函数,帮助我们运行actor;在C语言层面,只有一个回调函数;在lua层面,有多个回调函数,不同类型的消息调用不同的回调函数,比如网络消息、actor之间的消息;
-
消息队列
actor消息
消息的类型:actor之间的消息,网络消息、定时消息。
网络消息的事件如何与actor绑定?
skynet以消息的方式运行,所有的任务都以消息的方式进行传递。
skynet 通过 socket.start(fd, func) 来完成 actor 与 fd 的绑定;
底层epoll中,是通过epoll_ctl
设置struct epoll_event
中 data.ptr = (struct socket *)ud
; 来完成fd 与 actor绑定;
actor之间的消息
消息怎么一一对应上?
一个actor要往另一个actor中发消息,只是将消息放入目的actor的消息队列中,是一个move的操作。
定时消息
skynet中有一个单独线程来处理定时任务,使用时间轮算法。
当定时任务被触发,会将定时任务包装成消息,传递给产生这个任务的actor,从而驱动actor的运行。
actor的调度
actor的运行是由线程池驱动的。
线程池的调度
线程调度方式,是指队列中的任务从无到有,是怎么处理的, 消费线程如何调度? 需要怎么唤醒消费者线程?如果任务队列中的任务从有到无,需要怎么让消费者线程休眠?
消息生产者:网络线程、定时线程、工作线程
消息消费者:工作线程
工作线程
memcached使用的自旋锁
nginx解决惊群,使用自旋锁(cas实现自旋锁),锁存放在共享内存中。
关于mutex,spinlock,请参考《锁和原子操作的实现》。
线程调度方式
什么时候调用pthread_cond_signal?
有消息产生的时候,需要signal。
什么时候调用pthread_cond_broadcast?
线程池要退出的时候,需要broadcast。
signal在哪里调用?
网络线程产生消息,需要wakeup
定时线程产生消息,需要wakeup
actor之间的消息,不需要wakeup
actor的调度
worker线程池针对的是全局消息队列,全局消息队列组织的是活跃的actor的消息队列;活跃的actor是指actor的消息队列中有消息
协程
为什么要引入协程?
需要用协程来消除回调。
skynet,一个消息对应一个协程;openresty,一个请求对应一个协程。