LF模式解决的问题
一说起Leader/Followers并发模式,都会与Half-Async/Half-Sync并发模式进行比较,说LF模式更加高性能,成了一个高性能名词标签
符号,相反HA/HS仿佛成了一个低性能的名词标签,如果你的线程池不使用LF模式就谈论不上高效,要是你还在使用HA/HS模式,马上笼
统地建议换成LF模式,一切的问题会归根于HA/HS模式。那么为什么LF模式没有成一个标准的默认的并发模式呢,彻底取代其它并发模
式呢。因为Leader/Followers并发模式是设计用来解决Half-Async/Half-Sync并发模式在特定的使用场合中遇到的问题的。当使用
Half-Async/Half-Sync并发模式的变种Half-Reactive/Half-Sync的时,会伴随以下问题。
Half-Reactive/Half-Sync即,由一个线程进行Reactor(反应器)进行同步事件分离程序以及同步事件处理分派程序。事件处理程序可
以通过线程池共享队列,交给可以进行同步操作的线程进行。同步IO在Reactive线程中执行,事件后续处理交给Sync线程(s)。在Web服
务器,Reactive线程阻塞等待请求(数据接收)事件,并不阻塞地同步接收请求,将会因为流控而阻塞或大负载的文件输送操作移交到
Sync线程池的某一线程进行。
问题一:
这样的实现版本中就只有Reactive线程一个在进行同步I/O操作,虽然有多个句柄的同步指示事件通知不再阻塞,但却不能充分发挥多
线程将这些不再阻塞的句柄并发同步I/O操作。如果将同步I/O读取操作也移交Sync线程池的话,Reactive线程必须同步这些并发,等待
这些并发完成后再进行新一轮的同步事件分离程序等待。
问题二:
请求要输送到其它线程,所以必须使用堆复制请求。由于在多处理器环境中,线程的切换往往意味着处理器的切换,而切换处理器就会
有内存同质的问题,或者缓存(L1,L2 cache或MMU快表TLB)重新装载(即不亲和coherency一致)。简单地可以想像在内存寻址时,
CPU缓存缓存了内存页表及内存页表项,以便不用访问内存中的页表和页表项进行二次或多次内存访问。如果同步IO读出请求可以在同
一线程上处理,就可以尽量避免处理器的切换。但是这时又引入了另一个问题。要是所有线程都可以进行Reactor等待事件就好了。
问题三:
Reactor使用的同步事件分离程序,如select等往往是不可以对相同的句柄在多个线程进行阻塞等待的。如何能将同步事件通知不再阻
塞的句柄上的同步I/O并发掉,又可以保证只有一个线程使用Reactor进行同步事件分离程序等待,那么Leader/Followers模式就是最适
合且自然的选择了。
Leader/Followers并发模式是设计用来解决Reactor(反应器)在实际应用中使用线程池进行事件处理而伴随的问题。在基于IOCP的
Proactor(前摄器)应用多线程的场合,Leader/Followers并发模式就并不必要同时也不是最合适的选择,Ice的ThreadPool就是一个
实例,尽管在其文档上标榜其使了LF模式。
一次请求事件处理中包含了两步操作:同步不阻塞I/O读出请求以及处理请求。HA/HS模式,将上面两步归为异步不阻塞和同步,将一次
请求事件处理分别在两层服务的线程上执行。而LF模式是想方设法让一次请求事件处理执行在同一线程内。但是在Proactor事件模式中
,Proactor分离的是完成事件,随后的事件处理并不包含同步不阻塞I/O读出请求,而是直接对请求进行处理。并且IOCP可以被多个线
程进行事件分离等待,而IOCP本质是一个完成事件队列,事件处理的线程其实在共享一个队列,将完成事件同步处理,而系统相当于异
步服务层执行了我们发起的异步IO操作,并将完成异步IO操作的完成事件放入到IOCP的完成事件队列。Proactor模式可以视作HA/HS模
式的扩展,这时并不需要使用LF模式。而Half-Sync/Half-Reactiver的中层队列层就相当于一个I/O的完成事件队列,所以在基于IOCP
的Proactor上使用LF模式线程就不必要了,因为这里没有LF模式所要解决的问题。
Half-Async/Half-Sync并发模式是一种并发关系的模式,并不等同于多线程或线程池。系统的socket协议栈和我们在应用程序中使用的
socket同步操作接口,应用了这一并发模式。系统的协议栈在进行网卡的硬中断处理程序时,不可阻塞或长时间执行,必须高效地完成
以网卡缓存的数据交换和控制。这就相当于Half-Async层。系统离开系统调用前进行软中断处理程序将数据在协议栈向上传递到传输层
的缓存队列中,从而唤醒我们在应用发起的同步I/O操作。
Half-Async/Half-Sync并发模式应用在线程池设计时,就分为三个层,Half-Async不阻塞服务层,Half-Sync允许阻塞服务层,中间由
一层队列服务层进行交互。通常有一个线程池进行Half-Sync的服务。但并不是说Half-Async就只能一个线程进行服务,如果服务于
Half-Async层的线程不用进行同步并发的话,一样可以使用多个线程并发于Half-Async层,原则只有一个不可以阻塞。Half-
Reactive/Half-Sync模式的线程池中只有一个Reactive线程,是因为Reactor(反应器)使用同步事件分离程序不可以被多个线程对相
同的句柄进行等待。
其实epoll使用了类似Proactor模式,但是它发起的不是异步IO,而是异步的事件轮询。虽然epoll称自己是异步事件,但它的
epoll_wait事件分离程序却是句柄的同步事件。epoll利用句柄的等待队列,插入一个等候并绑定与poll不一样的处理程序,处理程序
将有事件通知的句柄入队到就绪队列。epoll(epoll_ctl而不是epoll_wait)发起的是异步的轮询,而就绪队列发生的是异步轮询的完
成事件,这个完成事件中包含了句柄的同步事件通知。epoll_ctl已经向句柄的发起了异步轮询,epoll_wait则是等待在就绪队列上分
离轮询完成的事件。为什么不干脆将事件处理程序挂在句柄的等待队列上,因为等待队列的处理程序运行在内核态,回调往往意味着颠
倒控制,在内核态回调用户态的代码,对于系统来说这是一件愚蠢的事情。
在《ACE程序员指南》一书中,作者为了突出LF模式比HA/HS模式的优越,给了一个HA/HS模式的实例。在ACE中,线程类继承ACE_Task可
以同时获得独立的消息(泵)队列服务。位于HA层为Manager线程类,维护着HS层的Worker线程类。HA层与HS层之间的队列服务层并不
由共享的队列方式实现,而是使用Worker线程类的独立消息(泵)队列来充当。这样的结果是,Worker线程类阻塞在各自的消息队列上
,事件处理必须经由HA层的Manager选出一个Worker并向它的消息队列发送消息。Worker线程被唤醒后处理事件,并在完成后入队到
Manager的空闲线程队列中。而Manager在它的消息队列上收到消息后,只是选出空闲Worker并转送消息。在他的这个模式实例中,每个
消息都必须唤醒Manager线程,再由Manager线程选出Worker线程并唤醒处理消息。即每次消息都唤醒两个线程,增加上下文切换来突出
LF模式的优越。同时Manager在选出Worker线程的执行,和Worker线程重新入队Manager的空闲队列又增加了线程同步操作。
ACE框架中有几个版本的Reactor,也只为基于select的Reactor提供了ThreadPool(LF模式)的版本ACE_TP_Reactor(,还有
Dev_Poll_Reactor,用于linux的epoll以及solaris的/dev/poll),而基于Windows系统的WaitForMutilpleObjects版本的
ACE_WFMO_Reactor,并没有提供一个LF线程池,因为WFMO可以在多个线程上同时对相同的内核对象进行事件等待,这是select所不能做
到的。另外ACE_Proactor也就没有提供LF线程池。ACE框架最终还是将线程池和线程池的并发模式的决定权交给了框架的使用者,除了
ACE_TP_Reactor,因为这是一个经典的案例,利用LF模式解决基于select的Reactor使用多线程事件处理时遇到的问题。
在Ice项目中,ThreadPool的Reactor版本实现了LF模式,线程池使用了Reactor进行事件分离。而在ACE项目中,由基于*nix的事件分离
程序的Reactor的事件处理循环实现LF并发模式,但并不提供线程池,多线程只需要运行Reactor的事件处理循环。这是不一样的出发点
的设计。
LF模式解决的问题的更多相关文章
- LF模式是个坑,ZeroIce中间件让你体会这个痛
LF模式是个坑,一个小小的失误就可能使你的网络处理瘫痪,Ice就很好地展现了出来,换句话说,Ice中间件或是LF模式就是一个坑,如果你一不小心. LF模式的官方论文中,论述了此模式用于高性能网络并发模 ...
- 【Java EE 学习 19】【使用过滤器实现全站压缩】【使用ThreadLocal模式解决跨DAO事务回滚问题】
一.使用过滤器实现全站压缩 1.目标:对网站的所有JSP页面进行页面压缩,减少用户流量的使用.但是对图片和视频不进行压缩,因为图片和视频的压缩率很小,而且处理所需要的服务器资源很大. 2.实现原理: ...
- spring通过工厂模式解决页面耦合问题
spring通过工厂模式解决页面耦合问题
- 通过进入单用户模式解决linux中的rc.local修改后无法启动的问题
问题:本想将teamviewer这个软件随linux自启动,所以将其启动命令放在rc.local中,但是重启后发现linux启动不起来了,系统前面都是正常启动的,就是无法进入帐户登陆界面,无法输入ro ...
- Centos7.6进入挂载硬盘后,进入应急模式(emergency mode)而非图形模式解决方法
Centos7.6进入挂载硬盘后,进入应急模式(emergency mode)而非图形模式解决方法 话说某天我想在centos7.6中挂载个硬盘,结果刚在虚拟机中添加了一块硬盘,再次打开系统时,居然就 ...
- Hibernate:组合模式解决树的映射
树经常用来展示目录结构,那么在Hibernate中怎样解决树的映射问题呢? 先来看一个分销商的树形结构的例子 所有分销商 东北区 辽宁省 沈阳医药 吉林省 华北区 北京市 北京医药 河北省 华南区 那 ...
- vi 替换命令“找不到模式”解决
在linux vi编辑工具中使用替换命令操作时,会出现明明有匹配查找模式的数据.却报"找不到模式"问题. 原因是vi s///替换操作缺省针对行,若要生效,则须要将光标移动到指定行 ...
- 用工厂模式解决ASP.NET Core中依赖注入的一个烦恼
这是最近在实际开发中遇到的一个问题,用 asp.net core 开发一个后端 web api ,根据指定的 key 清除 2 台 memcached 服务器上的缓存.背景是我们在进行 .net co ...
- TFTP 1.68智能刷机全能版发布,TTL线在CFE模式解决BCM5357如斐讯FIR302B等产品变砖问题
TFTP 智能刷机从发布以来一直受广大刷机朋友的喜爱,也有很多人一直加我的Q问如何刷机? 在这里我要告诉大家一下,由于机型种类繁多,建议有遇到问题,直接百度,有空的时候我能回答我尽量回答,其他的爱莫能 ...
随机推荐
- POI读入Excel用String读取数值类型失真问题(精度丢失)
问题:POI读取Excel数值单元格时,读取的小数数值与真实值不一致 话不多说,直接上代码! public static String getRealStringValue(Cell cell) { ...
- java学习5-面向对象(下)
final修饰符: final用于修饰类.变量和方法. final修饰变量时,一旦获得了初始值就不可改变 1.抽象方法和抽象类 抽象方法与抽象类的规则: a.抽象方法和抽象类必须使用abstract修 ...
- 为什么重写equals必须重写hoshCode的基础分析
为什么重写equals必须重写hoshCode的基础分析 1.我们先来了解下原生的equals和hashCode代码 原生equals:它判断的是两个对象是否相等 原生hashCode值:它是根据内存 ...
- spring security原理-学习笔记2-核心组件
核心组件 AuthenticationManager,ProviderManager和AuthenticationProvider AuthenticationManager只是一个接口,实际中是如何 ...
- mysql数据备份之 xtrabackup
上一篇简单介绍了一下mysqldump进行数据库的备份和恢复,这一篇说一下另一种备份工具xtrabackup,在InnoDB事务引擎泛滥的时代,xtrabackup可以很好的支持数据库的热备份,这就很 ...
- 百万年薪python之路 -- RBAC角色权限设计
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联.简单地说,一个用户拥有若干角色,每一个角色拥有若干权限.这样,就构造成"用 ...
- Spring Cloud ---- 服务注册与发现(Eureka 找到了!找到了! 嘻嘻)
记录一下吧,为什么接触分布式.因为裸辞之后没有找到工作,好的公司都要求有分布式经验,但是我完全没有.在一次面试的时候,面试官说如果你会分布式架构的话,我可以把工资给你开高2.5,我就考虑着给我点时间, ...
- Java TCP协议字节处理工具类
1.使用 tcp 协议 读取 输入流的固定长度的字节数 public static byte[] getTcpSpecificBytes(BufferedInputStream bis,int len ...
- Day 3,学习的知识点
年龄 如何判断是否未成年人 age = input('请输入你的年龄:')#input=输入age = int(age)#int=转化为整型if age < 18: print('小妹妹你 ...
- fenby C语言 P11
else {} if {} #include int main() { int a=15; if(a%2==0) { printf("我是偶数!"); }else { printf ...