A-A+

Cwinux源码解析(二)

2014年01月06日 Linux环境编程 评论 5 条 阅读 1,698 次
摘要:

Cwinux Commander执行方式。Command设计模式,消息被处理的过程。

8. CwxCommander命令模式等相关类

前面在CwxThread线程类和CwxThreadPool/CwxThreadPoolEx线程池类中,描述了线程的两种执行方式。回顾如下:

线程依据构造函数传递的参数不同,有两种工作方式。第一种是执行已指定的某个函数,这种方式比较简单,与pthread的用法基本一致。第二种是通过Commander这种机制来完成具体的功能。这种方式比较复杂。

在Commander这种方式下,每个消息都有自己的类型, 每种类型的消息都有一个预设的操作对象来处理这个消息。Commander可以被视为类型到操作对象的映射关系管理者。当线程按Commander这种方式工作时,线程不断地从消息队列中取出消息,然后交给Commander分发处理。

Cwinux采用了Command设计模式,Commander等类就是具体的实现。

Command设计模式适用于如下的情形:

  1. 抽象出待执行的动作以参数化某对象。
  2. 在不同时刻指定、排列和执行请求。
  3. 支持取消操作。
  4. 支持修改日志。

Cwinux中,CwxCommander符合第一种情形。

对于第一种情形,例如,在Java GUI编程中,我们可以使用库中自带工具箱中的Menu类来添加一个菜单。该Menu类的开发者并不知道某个Menu的MenuItem对应的动作时什么,但可以指定一个Command类,这个Command类具有一个函数execute。Menu的使用者从Command派生一个子类ConcreteCommand,在ConcreteCommand的execute函数中实现具体的工作。这种例子中,Command模式是面向对象版本的回调函数机制。

Command的结构:

Command Pattern

CwxCommander,CwxCmdOp等类就是Cwinux中Command模式的实现。

在cmn/CwxCommander.h文件中定义了两个类 CwxCmdOp和CwxCommander。

CwxCmdOp代码如下:

CwxCmdOp有若干个处理函数,当前这些处理函数不做任何有用的工作。这是Command模式中的Command类,这些onXXX函数就是Command类的execute函数(这里有多个不同的execute函数)。

具体使用时,Cwinux的使用者从CwxCmdOp继承一个具体的子类,例如CwxEchoEventHandler,然后实现所关心的execute函数(例如onRecvMsg函数)。

 

下面是CwxCommander类的代码。

首先,CwxCommander类的功能:基于事件的SVR-ID(消息包中带有这个属性),实现事件与其处理Handle的映射。

数据结构:既然是实现一个映射关系,自然是通过map数据结构。这里是使用的hash_map。代码中有如下代码片段:

这段代码定义了一种类型,该类型类型名叫CwxEventCommandHash,它本质上是一个hash_map类型,hash_map的前两个模板是常用的Key的类型和Value的类型,第三个模板是用来指定一个哈希函数,hash_map在维护内部数据结构的时候使用这个哈希函数。注意到,CwxEventCommandHash的Key类型是CWX_UINT32,这是SVR-ID的类型。SVR-ID是每个消息包都有的一个属性,CwxCommander使用这个属性进行分发。CwxEventCommandHash的Value类型是CwxCmdOp*类型。通过这种方式,就将SVR-ID到CwxCmdOp映射关系记录下来了。

 

另外,  typedef int (*fnEventApi)(CwxCmdOp* pEventOp, CwxMsgBlock*& msg, CwxTss* pThrEnv); 定义了一个函数指针类型(消息映射函数类型定义)。该函数指针指向的函数的参数是CwxCmdOp* pEventOp, CwxMsgBlock*& msg, CwxTss* pThrEnv三个参数,返回值是int型变量。

同时,代码 fnEventApi m_arrEvent[CwxEventInfo::SYS_EVENT_NUM + 1];///事件类型与处理API的映射 定义了一个函数指针数组。该数组依据事件类型对应到不同的函数指针。事件类型也是每个消息包的一个属性。Cwinux中有若干中事件类型:

这些事件类型会对应到CwxCommander的几个静态函数onRecvMsg,onConnCreated等(该对应关系在CwxCommander构造函数中设置)。每一个类型都对应于Command模式中的Command类的一个execute函数,这也是为何Cwinux中有多个execute函数。

 

好了,先暂停回顾一下。

每个消息都有SVR-ID和事件类型两个属性。

CwxCommander保存了SVR-ID到具体的CwxCmdOp的映射关系,同时每一个CwxCmdOp都有对应于不同事件类型的操作函数。

所以,每一个消息,交给CwxCommander后,CwxCommander能够依据消息的这两个属性找到一个具体的操作函数。

 

接着CwxCommander代码继续。

CwxCommander保存了SVR-ID到具体的CwxCmdOp的映射关系。在使用时,首先要先注册这种关系。regHandle就是实现这个功能。

其中,getEventOp是检查是否已经注册过了。代码如下:

CwxCommander的重头戏在dispatch函数。dispatch函数将消息分发给消息属性对应的(消息属性决定)Handle(CwxCmdOp的子类)。

dispatch函数中,首先根据消息的SVR_ID属性查找对应的CwxCmdOp类型的Handle,然后根据消息的事件类型属性查找对应的操作函数,最后调用该操作函数(CwxCommander的几个静态函数)。CwxCommander的这几个静态函数实际执行的是Handle对应的操作函数。

综上,CwxCommander及相关类逻辑结构如图示。

Commander_newsize

结合线程池的实现,将上述内容概述如下:

用户(使用Cwinux的应用开发者)在应用初始化时建立线程池,并创建Commander。然后实现CwxCmdOp的子类XxxHandle用于处理消息。用户将XxxHandle注册到Commander中。用户从网络上接收到的消息包,并投入到线程池中的消息队列中。线程池会从消息队列中不断地读取消息并通过Commander方式执行dispatch函数将消息分发到具体的操作函数来处理该消息。

5 条留言  访客:2 条  博主:2 条   引用: 1 条

  1. 大是大非

    作为一个类中的两个函数的话,
    例如一个是add
    一个是epoll
    在epoll的时候突然add?这个怎么弄呢?或许不需要锁。。嘿嘿

    • LoopJump

      你是说,epoll_wait和add同时发生么?
      这种情况下,好像是可能发生的。但是应该不需要锁吧。
      我以前粗略地看过epoll的内核实现,epoll_wait发现没有IO事件,会睡眠并让出CPU。此时如果执行add的话,主要就是在epoll_create创建的红黑树上添加一个节点(代表新添加的这个fd),然后注册回调函数(新添加的fd有IO事件时调用,用于唤醒epoll_wait)。看起来,似乎epoll_wait阻塞和add对应的相关操作同时发生不会有什么问题。
      ps,我不是很确定。

  2. 大是大非

    有一个小小的问题,我在想在网络编程中,拿epoll来说,一边需要poll某个fd,一边还要等着
    add某个fd,这可能会在某个情况下出现冲突。你如果解决呢?锁?或者说是来个备用队列?

    • LoopJump

      "add某个fd"指的是监听新来的网络连接么?通常的代码是将执行了listen & bind的listen_fd加入epoll,有新连接进来时,这个listen_fd有可读事件发生,epoll_wait于是就返回了。
      这种方式和timerfd都是单线程的,应该不需要锁吧。
      ps,我也是菜鸟,了解比较浅。

来自外部的引用: 1 条

  • Cwinux源码解析系列 | 薛途的博客

给我留言