简单的说,半同步/半异步的模式,是在IO线程层面异步,把数据塞进一个同步队列,然后demultiplexing,一堆线程同步地处理数据。这个模型中,IO层面用异步,相比一个线程一个链接的模式,省去了很多线程维护开销,但由于队列资源是线程共享内容,在进队列/出队列的时候,免不了会有race condition。
leader/follower解决的问题主要就是IO demultiplexing中不够有效率的地方,leader线程去监听IO,避免多个线程监听造成的race condition,有数据需要处理时,如果自己处理,就叫其他空着的follower去做leader;如果要follower处理,自己可以继续做leader。任何时间,系统都只有一个leader线程在监听IO,而处理IO的,可以有多个线程(通常是一个线程池)。当所有线程在忙的是,IO事件会自动排队,等待leader空出来处理。
许多餐厅使用 半同步/半异步 模式的变体。例如,餐厅常常雇佣一个领班负责迎接顾客,并在餐厅繁忙时留意给顾客安排桌位,为等待就餐的顾客按序排队是必要的。领班由所有顾客“共享”,不能被任何特定顾客占用太多时间。当顾客在一张桌子入坐后,有一个侍应生专门为这张桌子服务。
领导者/追随者(Leader/Followers):
在日常生活中,领导者/追随者模式用于管理许多飞机场出租车候车台。在该用例中,出租车扮演“线程”角色,排在第一辆的出租车成为领导者,剩下的出租车成为追随者。同样,到达出租车候车台的乘客构成了必须被多路分解给出租车的事件,一般以先进先出排序。一般来说,如果任何出租车可以为任何顾客服务,该场景就主要相当于非绑定句柄/线程关联。然而,如果仅仅是某些出租车可以为某些乘客服务,该场景就相当于绑定句柄/线程关联。
在 《POSA2》 书中列举的例子都比较复杂,并且书上没有列出完整的代码。但是这两个模式其实都可以在《unix网络编程》一书中找到对应的完整的代码和相关的讨论。
在 半同步/半异步 模式中,需要由模式实现者显示构造一个队列,以便同步层和异步层可以通信。
在 《unix网络编程》 一书的 “27.12 TCP预先创建线程服务器程序,主线程统一 accept” 的例子中,如果只是从处理 accept 这个事件上看,可以认为这是一个使用了 半同步/半异步 模式的例子。但是从具体的业务处理(即web_child的处理上),仍然可以认为是一个ThreadPerConnection模型,因为在 thread_main 中直接读取请求和发送响应。在这个例子中,就有一个队列:
- int clifd[MAXNCLI], iget, iput;
- int main( int argc, char * argv[] )
- {
- ......
- int listenfd = Tcp_listen( NULL, argv[ 1 ], &addrlen );
- ......
- iget = iput = 0;
- for( int i = 0; i < nthreads; i++ ) {
- pthread_create( &tptr[i].thread_tid, NULL, &thread_main, (void*)i );
- for( ; ; ) {
- connfd = accept( listenfd, cliaddr,, &clilen );
- clifd[ iput ] = connfd; [b]// 接受到的连接句柄放入队列[/b]
- if( ++iput == MAXNCLI ) iput = 0;
- }
- }
- void * thread_main( void * arg )
- {
- for( ; ; ) {
- while( iget == iput ) pthread_cond_wait( ...... );
- connfd = clifd[ iget ]; [b]// 从队列中获得连接句柄[/b]
- if( ++iget == MAXNCLI ) iget = 0;
- ......
- web_child( connfd );
- close( connfd );
- }
- }
而在 领导者/追随者 模式中,同样是有一个队列的,不过不需要模式实现者显示构造,而是直接使用了操作系统底层的队列。
在 《unix网络编程》 一书的 “27.11 TCP 预先创建服务器线程,每个线程各自 accept ” 的例子中,就是直接使用了操作系统中关于 accept 的队列。这个例子可以认为是 领导者/追随者 模式的一个例子。
- int listenfd;
- int main( int argc, char * argv[] )
- {
- ......
- listenfd = Tcp_listen( NULL, argv[ 1 ], &addrlen );
- ......
- for( int i = 0; i < nthreads; i++ ){
- pthread_create( &tptr[i].thread_tid, NULL, &thread_main, (void*)i );
- }
- ......
- }
- void * thread_main( void * arg )
- {
- for( ; ; ){
- ......
- [b]// 多个线程同时阻塞在这个 accept 调用上,依靠操作系统的队列[/b]
- connfd = accept( listenfd, cliaddr, &clilen );
- ......
- web_child( connfd );
- close( connfd );
- ......
- }
- }
http://it.taocms.org/06/837.htm
HS/HA 半同步/ 半异步模式 :分为三层,同步层、队列层、异步层,又称为生产者消费者模式,主线程处理I/O事件并解析然后再往队列丢数据,然后消费者读出数据进行应用逻辑处理;
优点:简化编程将低层的异步I/O和高层同步应用服务分离,且没有降低低层服务性能。集中层间通信。
缺点:需要线程间传输数据,因此而带来的动态内存分配,数据拷贝,语境切换带来开销。高层服务不可能从底层异步服务效率中获益。
L/F 领导者跟随者模式 :在LF线程池中,线程可处在3种线程状态之一: leader、follower或processor。处于leader状态的线程负责监听tb网络端口,当有消息到达时,该线程负责消息分离,并从处于 follower状态中的线程中按照某种机制如FIFO或基于优先级等选出一个来当新的leader,然后将自己设置为processor状态去分配和处 理该事件。处理完毕后线程将自身的状态设置为follower状态去等待重新成为leader。在整个线程池中同一时刻只有一个线程可以处于leader 状态,这保证了同一事件不会被多个线程重复处理。
缺点:实现复杂性和缺乏灵活性;
优点:增强了CPU高速缓存相似性,消除了动态内存分配和线程间的数据交换。