利用做饭的流程来解释多线程的应用
单线程 & 单人做饭
单人做饭的流程 (我的流程)
- 选择吃什么
- 买菜,买肉
- 做米饭
- 洗肉(解冻肉),洗菜,择菜。
- 切肉
- 切菜, 切辣椒,葱花,姜片。
- 炸肉,下花椒八角, 老抽,醋
- 把肉盛出来 (或者不盛)
- 下葱姜, 炒香后下辣椒
- 下蔬菜
- 放调味料
- 关火, 盛出来
- 盛米饭,吃饭
单线程
这里假定任务是处理一个玩家购买金币的 socket 请求
- 获取网络数据
- 解析成我们需要的数据格式
- 从数据库/缓存取出玩家信息
- 对玩家数据进行上锁
- 获取配置表数据 (花多少钻石可以购买多少金币)
- 检测玩家的钻石是否足够
- 修改钻石和金币的数量
- 保存玩家的数据到数据库/缓存, 并解锁玩家数据
- 产生响应结果
- 发送数据给客户端
多线程 & 餐馆做饭
餐馆做饭
根据职责进行人员的分配,配合整个做菜流程
按职责分成下面几个角色。
- 采购员
- 案板
- 打杂
- 厨师
- 服务员
- 客人
每个角色的职责大致如下。
- 采购员 (买菜,买肉)
- 案板 (切菜, 切肉)
- 打杂 (做米饭,配菜,送菜给服务员,拿盘子等)
- 厨师 (炒菜)
- 服务员 (把菜从厨房间端到客人面前,给客人盛米饭)
- 客人 (吃饭)
需要的人员大大增加了, 每个人员负责的任务大大减少了。
多线程
根据职责分成下面几类线程 (每类线程的数量可能大于1)
- 网络线程
- 业务线程
- 数据库操作线程
每种线程的职责大致如下。
- 网络线程
- 从网络中获取数据
- 解析数据成我们需要的格式, 碰到无效数据则丢弃
- 业务线程
- 从数据库中取出玩家数据
- 处理业务
- 发送响应给客户端 (此处应该没有真的发送, 而是放入了网络数据的缓存里面)
- 数据库操作线程
- 定时保存数据到数据库
- 处理好数据加锁以及缓存 (笔者假设的内容,并没有真的实现过)
需要的线程增加了, 每类线程负责的内容变少了。
总结
做饭复杂化有什么好处和代价呢?
代价
- 这里先说一下代价
- 需要的人变得很多了
- 原本需要一个人,现在至少需要6个人
- 会增加沟通的成本,管理费等。
- 整个团队(客人除外) 很可能会受到木桶原理的限制
- 当客人数量少的时候, 这套结构是复杂且浪费的。
好处
- 好处就是可以更快的制作出饭菜,服务客人。
- 当客人是2-3个的时候,一个人做饭还好
- 当客人是4-8个的时候,一个人做饭就会很吃力
- 当客人是30个的时候,一个人完全忙不过来, 很多客人会处于等待接应,极其愤怒的状态。
- 但是切换成这套机制下, 30个人也还好,完全忙的过来。 并且,大多数情况下,客人会得到来自服务员的响应,少部分情况下才会出现无人搭理的情况。
- 而且还可以根据情况进行人员数量的调整。
- 如果做饭比较慢,则增加厨师的数量
- 如果配菜比较慢,则增加打杂的数量
- 等等。。。
多线程比起单线程有什么好处和代价呢?
代价
- 同样, 还是先说一下代价
- 编写起来比较复杂
- 需要处理多线程数据同步, 以及锁的问题。
- 可能会碰到更多的 更复杂的 bug 。
好处
- 充分利用计算机多核优势
- 可以同时处理更多的请求
- 单线程情况下, 同时只能处理一个请求,多线程则同时能处理多个请求
- 单线程情况下,在处理任务中的时候,后续请求会卡在网络层里等待。 多线程模型下,数据会从网络层拿出来, 封装成我们需要的数据格式, 并丢弃无效请求。
- 理论上来说, 大部分应用程序的处理请求的并发,多线程的极限比单线程大的多得多。
- 解耦
- 单线程程序在编写的时候也可以解耦, 但是图方便可能很少些单线程的会那样做😂
- 在 GUI 程序下, 单线程情况下, 如果碰到一个比较重的任务时(需要执行3-10s) ,UI 会卡住,在表现上是整个程序卡住了, 会给人非常不好的感觉。 把这个重的任务放到别的线程执行,则不会卡住 UI 线程, 用户会获取较好的体验✔️
- 请求多, 增加网络线程的数量。 业务处理耗时比较久,增加业务处理的线程。 (注意: 线程调度切换也是需要代价的, 并不是越多的线程越好,但是适当的数量增加肯定是好的)
那么,什么时候用呢?
笔者给出的建议是根据需求来决定。
比如, 做一个很小的程序的时候, 单线程开发比较快, 当然选择单线程了。
开发大型程序(网游服务端,对并发有要求) 肯定要使用多线程模型的。