多路分发

有的时候程序要处理多种类型的交互,如果只使用单向的绑定支持,那么程序会变得相当复杂。比如,有A,B,C三种种类型互相比较,抽象出来的算法a.compete(b),可是a,b可以是A,B,C中的任意两种类型的互相比较,这就麻烦了。

java的动态绑定其实是一种单路分发,能动态确定a的类型,不能够动态绑定b的类型,传统的方法就是重载compete方法。代码繁琐,还有些丑。

多路分发是一种技巧,可以让代码优雅 :) ,用实现“锤子、剪刀、布”的程序为例子:

 

 

写道
import java.util.Random;

public class RoShamBo{
static Random rand = new Random(4);

public static void main(String[] args){
for(int i = 0; i < 10; i++){
Item a = nextItem();
Item b = nextItem();
System.out.println(a + " vs " + b + " : " + b.compete(a));
}

}

static Item nextItem(){
switch(rand.nextInt(3)){
default:
case 0: return new Rock();
case 1: return new Scissors();
case 2: return new Paper();
}
}
}

enum Result{
WIN, DRAW, LOSE;
}

interface Item{
Result compete(Item e);
Result eval(Paper p);
Result eval(Scissors s);
Result eval(Rock r);
}

class Rock implements Item{
@Override
public Result compete(Item e){
return e.eval(this);
}

@Override
public Result eval(Paper p){
return Result.LOSE;
}

@Override
public Result eval(Scissors s){
return Result.WIN;
}

@Override
public Result eval(Rock r){
return Result.DRAW;
}

@Override
public String toString(){
return "Rock";
}

}

class Scissors implements Item{
@Override
public Result compete(Item e){
return e.eval(this);
}

@Override
public Result eval(Paper p){
return Result.WIN;
}

@Override
public Result eval(Scissors s){
return Result.DRAW;
}

@Override
public Result eval(Rock r){
return Result.LOSE;
}

@Override
public String toString(){
return "Scissors";
}
}

class Paper implements Item{

@Override
public Result compete(Item e){
return e.eval(this);
}

@Override
public Result eval(Paper p){
return Result.DRAW;
}

@Override
public Result eval(Scissors s){
return Result.LOSE;
}

@Override
public Result eval(Rock r){
return Result.WIN;
}

@Override
public String toString(){
return "Paper";
}
}

 

 

### IO多路复用与多路事件分发器的实现原理 #### 1. **IO多路复用概述** IO多路复用是一种允许单个线程管理多个文件描述符的技术,通常用于高效处理大量并发连接。在这种模型下,一个线程可以监控多个文件句柄的状态变化,并在任意一个文件句柄准备好进行读写操作时得到通知[^1]。 #### 2. **多路事件分发器的作用** 多路事件分发器的核心功能在于通过高效的机制来管理和调度不同类型的事件(如可读、可写或异常)。它利用操作系统提供的接口(如`select`、`poll`或`epoll`),将多个文件描述符注册到内核中并等待其状态的变化。一旦某个文件描述符变为就绪状态,分发器会立即将该事件传递给对应的回调函数以完成进一步的操作[^5]。 #### 3. **实现方式** 以下是几种常见的多路事件分发器及其工作原理: - **Select** `select` 是一种早期的多路复用方法,支持同时监听多个文件描述符的最大数量有限制(通常是1024)。每次调用都需要传入所有的文件描述符列表,即使其中大部分可能并未发生变化。这种方式效率较低,因为无论是否有实际活动发生,都会对所有文件描述符逐一检查[^4]。 - **Poll** 类似于 `select`,但没有最大文件描述符数量的限制。然而,它的性能瓶颈仍然存在于需要遍历整个文件描述符集合上,即便只有少数几个处于活跃状态。 - **Epoll** Linux下的 `epoll` 提供了一种更先进的解决方案。相比前两者,`epoll` 使用的是基于事件驱动的方式,只会在真正存在待处理的数据时才触发回调。具体来说: - 它分为三个主要阶段:`epoll_create` 创建实例;`epoll_ctl` 添加/修改/删除感兴趣的文件描述符;`epoll_wait` 等待事件的发生。 - 当某些文件描述符上有新数据到达时,内核会主动将其加入到准备好的队列里而无需应用层再次扫描全部资源。 #### 4. **代码示例** 下面展示了一个简单的使用 C++ 结合 epoll 的例子,说明如何构建一个多路事件分发器: ```cpp #include <sys/epoll.h> #include <unistd.h> #include <iostream> #define MAX_EVENTS 10 int main() { int epoll_fd = epoll_create1(0); struct epoll_event event; struct epoll_event events[MAX_EVENTS]; int fd_to_watch = STDIN_FILENO; // 监听标准输入流作为示范 event.events = EPOLLIN | EPOLLET; // 设置为边缘触发模式 event.data.fd = fd_to_watch; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_to_watch, &event); while (true) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); // 阻塞直到有事件到来 if (nfds == -1) { perror("epoll_wait"); break; } for(int n = 0; n < nfds; ++n){ if(events[n].data.fd == fd_to_watch && (events[n].events & EPOLLIN)){ char buf[1024]; ssize_t bytes_read = read(fd_to_watch,buf,sizeof(buf)-1); if(bytes_read > 0){ buf[bytes_read]='\0'; std::cout << "Received data: "<<buf<<std::endl; } } } } close(epoll_fd); } ``` 此程序展示了基本的 epoll 循环逻辑,其中包括初始化 epoll 文件描述符、向其添加要监视的目标文件描述符以及持续循环检测是否有任何已注册的文件描述符产生了指定条件下的 I/O 就绪信号。 --- ### 性能优化考虑因素 为了提高多路事件分发器的整体表现,在设计时还需要关注以下几个方面: - 减少上下文切换次数; - 利用零拷贝技术降低内存带宽消耗; - 对频繁使用的缓冲区采用预分配策略减少动态申请频率等措施都可以有效提升系统的吞吐量和响应速度[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值