这里提到的两个设计模式都是用于高并发系统(例如一个高性能的网络服务器)的。这里我只是简单地提一下: 1. 半同步/半异步(half-sync/half-async ):
三、 半同步-半异步模型 http://www.cppblog.com/liangairan/articles/62917.html?opt=admin 1. 话说一个地方有一群有组织无纪律的人从事山贼这个很有前途的职业。 2. 他们有一个山贼头头,他专门负责望风,其他的喽罗待命。 3. 假如发现有落单的过往客商,山贼头头会到路口拦路,让客商双手抱头蹲在地上,然后让一个小喽罗为这个倒霉鬼"服务"。 4. 假如客商很多,山贼头头会让客商在地上蹲成一排(严肃点,排队啦,打劫啦)。 一群小喽罗挨个为大家"服务"。 5. 头头的工作很重要,对于每个客商他都不会花费太多时间,拦路以后,他会让客商排队等待打劫。 6. 过往客商太多而山贼数量不够,客商的排队可能需要等待较长的时间。
在网 上一份资料 中引用了一本貌似很经典的书 里的比喻: ” 许多餐厅使用 半同步/半异步 模式的变体。例如,餐厅常常雇佣一个领班负责迎接顾客,并在餐厅繁忙时留意给顾客安排桌位, 为等待就餐的顾客按序排队是必要的。领班由所有顾客“共享”,不能被任何特定顾客占用太多时间。当顾客在一张桌子入坐后, 有一个侍应生专门为这张桌子服务。 “ 按照另一份似乎比较权威的文档的描述,要实现半同步/半异步模式,需要实现三层:异步层、同步层、队列层。因为很多操作 采用异步方式会比较有效率(例如高效率的网络模型似乎都采用异步IO),但是异步操作的复杂度比较高,不利于编程。而同步 操作相对之下编程要简单点。为了结合两者的优点,就提出了这个模式。而为了让异步层和同步层互相通信(模块间的通信),系 统需要加入一个通信队列。异步层将操作结果放入队列,同步层从队列里获取操作结果。 回过头来看看我之前写的那个select网络模型代码,个人认为基本上算是一个半同步半异步模式的简单例子:Buffer相当于通信 队列,网络底层将数据写入Buffer,上层再同步地从该队列里获取出数据。这样看来似乎也没什么难度。 = = 关于例子代码,直接引用iunknown给的: //这就是一个典型的循环队列的定义,iget 是队列头,iput 是队列尾</STRONG> 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.thread_tid, NULL, &thread_main, (void*)i ); for( ; ; ) { connfd = accept( listenfd, cliaddr,, &clilen ); clifd[ iput ] = connfd; // 接受到的连接句柄放入队列</STRONG> if( ++iput == MAXNCLI ) iput = 0; } } void * thread_main( void * arg ) { for( ; ; ) { while( iget == iput ) pthread_cond_wait( ...... ); connfd = clifd[ iget ]; // 从队列中获得连接句柄</STRONG> if( ++iget == MAXNCLI ) iget = 0; ...... web_child( connfd ); close( connfd ); } }
http://blog.csdn.net/cjfeii/article/details/17267487
众所周知,同步模式编程简单,但是I/O的利用利率低;而异步模式编程复杂,但是I/O利用率高。 综合同步异步的有优点,就有了半同步半异步的设计模式。 这个模式中,高层使用同步I/O模型,简化编程。低层使用异步I/O模型,高效执行。
half sync/half async可以很好的使得"变成复杂度"和"执行效率"之间达到一种平衡.
半同步半异步模式在下面的场景中使用:
系统必须响应和处理外部异步发生的事件, 如果为每一个外部资源的事件分派一个独立的线程同步处理I/O,效率很低。 如果上层的任务以同步方式处理I/O,实现起来简单。
上层的任务(如:数据库查询,文件传输)使用同步I/O模型,简化了编写并行程序的难度。 而底层的任务(如网络控制器的中断处理)使用异步I/O模型,提供了执行效率。
一般情况下,上层的任务要比下层的任务多,使用一个简单的层次实现异步处理的复杂性,可以对外隐藏异步处理的细节。另外,同步层次和异步层次任务间的通信使用一个队列来协调。
可以分为三层:同步任务层,队列层,异步任务层。
本层的任务完成上层的I/O操作,使用同步I/O模型,通过队列层的队列中传输数据。和异步层不同,同步层的任务使用活动对象执行,这些活动对象有自己运行栈和寄存器状态。当执行同步I/O的时候,他们会被阻塞/睡眠。
这个层在同步任务层和异步任务层之间,提供了同步控制和缓存的功能。异步任务的I/O 事件被缓存到消息队列中,同步任务层在队列中提取这些事件(相反方向亦然)
处理低层的事件,这些事件由多个外部的事件源产生(例如网卡,终端)。和异步任务不同,此层的实体是被动对象,没有自己的运行栈,要求不能被阻塞。
上层的任务被简化 不同层可以使用不同的同步策略 层间的通信被限制在单独的一点,因为所有的交互使用队列层协调。 在多处理器环境中提高了性能。
跨边界导致的性能消耗,这是因为同步控制,数据拷贝和上下文切换会过度地消耗资源。
上层任务缺少异步I/O的实现。
领导者-追随者模型 http://www.cppblog.com/liangairan/articles/62917.html?opt=admin 打比方: 1. 话说一个地方有一群有组织无纪律的人从事山贼这个很有前途的职业。 2. 一般就是有一个山贼在山路口察看,其他人在林子里面睡觉。 3. 假如发现有落单的过往客商,望风的山贼就会弄醒一个睡觉的山贼,然后自己去打劫。 4. 醒来的山贼接替作望风的事情。 5. 打劫的山贼搞定以后,就会去睡觉,直到被其他望风的山贼叫醒来望风为止。 6. 有时候过往客商太多,而山贼数量不够,有些客商就能侥幸平安通过山岭(所有山贼都去打劫其他客商了)。 计算机版本 1. 有若干个线程(一般组成线程池)用来处理大量的事件 2. 有一个线程作为领导者,等待事件的发生;其他的线程作为追随者,仅仅是睡眠。 3. 假如有事件需要处理,领导者会从追随者中指定一个新的领导者,自己去处理事件。 4. 唤醒的追随者作为新的领导者等待事件的发生。 5. 处理事件的线程处理完毕以后,就会成为追随者的一员,直到被唤醒成为领导者。 6. 假如需要处理的事件太多,而线程数量不够(能够动态创建线程处理另当别论),则有的事件可能会得不到处理。 同样,给出别人引用的比喻: ” 在日常生活 中,领导者/追随者模式用于管理 许多飞机场出租车候车台。在该用例中,出租车扮演“线程”角色,排在第一辆的出 租车成为领导者,剩下的出租车成为追随者。同样,到达出租车候车台的乘客构成了必须被多路分解给出租车的事件,一般以先进 先出排序。一般来说,如果任何出租车可以为任何顾客服务,该场景就主要相当于非绑定句柄/线程关联。然而,如果仅仅是某些 出租车可以为某些乘客服务,该场景就相当于绑定句柄/线程关联。 “ 其实这个更简单,我记得<unix网络编程>中似乎提到过这个。总之有一种网络模型(connection-per-thread?)里,一个线程用于 accept连接。当接收到一个新的连接时,这个线程就转为connection thread,而这个线程后面的线程则上升为accept线程。这里, accept线程就相当于领导者线程,而其他线程则属于追随者线程。 iunknown 的例子代码: 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.thread_tid, NULL, &thread_main, (void*)i ); } ...... } void * thread_main( void * arg ) { for( ; ; ){ ...... // 多个线程同时阻塞在这个 accept 调用上,依靠操作系统的队列</STRONG> connfd = accept( listenfd, cliaddr, &clilen ); ...... web_child( connfd ); close( connfd ); ...... } }