1)多线程方式利用多核心
锁(信号量、互斥锁、自旋锁、读写锁、原子锁cas、乐观锁、悲观锁)
锁的粒度
什么情况下用什么锁,比较困惑
消息队列
管道(不需要加锁): pipe+epoll/select/epoll
资源+锁
并发实体:线程
总结:
需要大量的锁。
2)多进程方式利用多核心
共享内存
pipe管道、
父进程fork子进程有限制
只能一端读一端写
数据是单向的
socket:
一般用这种来
用epoll
信号
并发实体:进程。
总结:
3)csp模型
go语言:
并发实体:goroutine(协程,用channel来通信)。
协程是轻量级的线程。
协程切换比较轻量,是开发者自己调度的。
线程切换需要进程,进程切换需要。
总结:
优化多线程的编程方式。
4)actor
erlang:
语言层面实现actor模型。
把actor命名为进程,actor是轻量级的进程。
skynet:
从框架层面实现actor。
actor由程序来调度,比较轻量。
提几个问题:
1.actor之间是如何沟通的?
统一性差,隔离性强。
独立的运行环境。
a actor和b actor沟通,就发送到对方的消息队列中。
多进程是socket沟通,其实也是消息的方式。
2.actor是怎么运行的?
多进程中,有一个主线程,通过os调度来运行。
actor中,当消息队列中有消息的时候,就可以运行:
多个线程,消耗actor消息。 工作线程。轮训,消息队列取出消息,并在actor
环境中运行了。
取出消息,并且调用回调函数。
3.actor是如何调度的呢?
不是每个actor都有消息。这个怎么处理呢?
全局消息队列。把actor中的消息插入全局消息队列,指针的方式。
通过多线程消费全局消息队列的方式来调度actor,这样范围缩小,效率提高。
将有消息的消息队列放入全局消息队列中,工作线程只需要轮训全局消息队列,
就可以对整个actor消息进行调度了。
4.actor怎么接收网络中的数据?
fd和actor绑定
epoll_create
红黑树+就绪队列
epoll_ctrl
epoll_event, fd, actor_id
epoll_wait(ev[])
取出一个数组
ev[].data.ptr -->actorid, fd--》找到actor--》网络线程发送数据到actor
5.如何使用skynet?
agent--》玩家actor
hall--》大厅actor
main--》启动actor
redis---》玩家不能退出,重新进去要保存数据。 redis actor
room--》房间actor,3人一个房间组成的actor
./skynet/skynet config.test
main
redis
hall
把功能拆分成多个actor, 热点拆分为多个actor,
一个actor就是一个统一性较差的环境。
架构:
main:网关--》listenfd
agent: connedfd--》其它数据全部由agent进行处理
游戏是相同的,我们不能放到一个actor中处理,而要创建很多个节点,充分利用cpu
每一个client都有一个agent
hall:保存匹配队列的--》大厅
每一个agent与hall连接
hall去创建一个room actor
skynet中的2种调用方式:
send:消息队列send,异步调用。
call--》rpc: 阻塞,同步调用。
指导思想: 简单可用,逐步优化,忌讳过度优化
逻辑的完备性
actor:
隔离的环境(lua或内存块)
回调函数
消息队列
单线程来处理的
让工作线程去轮训只有消息的actor
总结:
优化的是多进程的解决方案。
---------------------------
并发编程的2个考虑:
隔离性
运行环境隔离:
多线程隔离性差、统一性高
统一性
数据的统一
------------------------------
分布式:
C:一致性
A:可用性
P:分区容错性
----------------------
源码:
nginx
redis
skynet
zeromq
造轮子:
中间件:
内存池
线程池
连接池
消息队列
高并发IO:
epoll
reactor
proactor
网络协议栈:
用户态,实现百万连接
nginx模块开发
并发设计
集群能力:
数据库的集群。
zookeeper的集群--》多进程的管理。
linux内核编程:
网卡子系统的编程
内存管理
进程间通信
内核的编译
性能测试:
数据库的索引优化和sql优化。
linux调优、内存调优。
部署能力:
docker
一线架构