P2psim源代码分析三

P2psim源代码分析三

Kejieleung

 

       继续上一篇事件发生器EventGenerator的分析,这部分重点分析事件队列EventQueue的设计。事件队列,作为Observer模式中的ConcreteSubject,首先复习一下Observer模式吧。

可参考:

http://www.cppblog.com/zliner/archive/2006/09/10/12217.html

有一篇简要介绍

 

观察者模式把目标对观察者的依赖进行抽象:使目标只知道自己有若干观察者,但不知道这些观察者具体是谁,可能有多少个;当目标状态改变时只要给这些观察者一个通知,不必作更多的事情。这样目标对观察者的依赖就达到了抽象和最小,而目标对具体观察者的依赖被解除了。

类图如下:

 

Subject对象保存一个Observer引用的列表,当我们让一个ConcreteObserver对象观察Subject对象时,调用后者的Attach()方法,将前者的引用加入该列表中。当Subject对象状态改变时,它调用自身的Notify方法,该方法调用列表中每一个ObserverUpdate()方法。一个ConcreteObserver只要重定义Update()就能收到通知,作为对通知的响应,Update()调用Subject对象的getStatus()获取数据,然后更新自身。当不需要继续观察时,ConcreteObserver对象调用Subject对象的Detach()方法,其引用被从列表中移除。

      

OK看完上面的部分后,即可看一下EventQueue的观察者模型的实现。可看下面类图

 

 

Observerd对应的就是Subject了,EventQueue对应ConcreteSubject,按照观察者模式,首先目标对象保存观察者对像,这部分的是由具体的实现ChurnEventGenerator的构造函数里调用:

  1. //kejie: 注册监视器
  2.   EventQueue::Instance()->registerObserver(this);
  3. EventQueue的完整class声明:
  4. class EventQueue : public Threaded, public Observed {
  5.   friend class EventQueueObserver;
  6. public:
  7.   static EventQueue* Instance();    //singletion
  8.   ~EventQueue();
  9.   void add_event(Event*);
  10.   Time time() { return _time; }
  11.   static Time fasttime() { return _instance?_instance->time():0; }
  12.   void go();
  13. private:
  14.   EventQueue();
  15.   struct eq_entry {
  16.     eq_entry() { ts = 0; events.clear(); }
  17.     eq_entry(Event *e) { ts = e->ts; events.clear(); }
  18.     Time ts;
  19.     vector<Event*> events;
  20.     sklist_entry<eq_entry> _sortlink;
  21.   };
  22.   skiplist<eq_entry, Time, &eq_entry::ts, &eq_entry::_sortlink> _queue;
  23.   static EventQueue *_instance;
  24.   Time _time;
  25.   Channel *_gochan;
  26.   virtual void run();   //kejie:线程函数
  27.   bool advance();
  28.   // for debuging
  29.   void dump();
  30. };

EventQueue这部分实现又使用了singleton单件模式,在第一次使用时实例化:

EventQueue*

EventQueue::Instance()

{

  if(_instance)

    return _instance;

  return (_instance = New EventQueue());

}

 

EventQueue的构造函数再创建一个底层channel和启动线程,关于channel模型的分析会放在后,这里先理解为底层线程队列的实现即可。注意这里的事件都是能过一个skiplist的数据结构来保存,具体实现在./p2psim/skiplist.h,因为是使用了template所以声明实现都放在这个文件里了。

 

EventQueue::EventQueue() : _time(0)

{

//kejie: Channel *_gochan;

  _gochan = chancreate(sizeof(Event*), 0);

  assert(_gochan);

 

//kejie:add self run to the threadmanage

  thread();

}

 

       这里最主要的就是run函数了,本身也作为一个总线程的线程函数,当EventQueue有多于一个事件到达时,就会调用advance()处理,这是一个线程触发函数,将事件分发到另一个线程中处理。

  1. //kejie:线程函数
  2. void
  3. EventQueue::run()
  4. {
  5.   // Wait for threadmain() to call go().
  6.   recvp(_gochan);
  7.   while(true) {
  8.     // let others run
  9.     while(anyready())
  10.       yield();
  11.     // time is going to move forward.
  12.     // everyone else is quiet.
  13.     // must be time for the next event.
  14.     // run events for next time in the queue
  15.     if(!advance())
  16.       break;
  17.   }
  18. }
  19. // moves time forward to the next event
  20. bool
  21. EventQueue::advance()
  22. {
  23. ...
  24.   for(vector<Event*>::const_iterator i = eqe->events.begin(); i != eqe->events.end(); ++i) {
  25.     assert((*i)->ts == eqe->ts &
  26.            (*i)->ts >= _time &
  27.            (*i)->ts < _time + 100000000);
  28.     // notify observers, who will not add eventsinto the eventqueue using EventQueueObserver::add_event
  29.    notifyObservers((ObserverInfo*) *i);
  30.     Event::Execute(*i); // new thread, execute(), delete Event
  31. ...
  32. }

在有事件到达时,通知EventQueueObserver更新状态,调用Observerdkick函数

void

Observed::notifyObservers(ObserverInfo *oi)

{

  if(!_hasObservers)

    return;

    for(set<Observer*>::const_iterator i = _observers.begin(); i != _observers.end(); ++i)

    (*i)->kick(this, oi);

}

kick函数是 Observer的纯虚函数(Update),通知所有Observer更新状态,为调用具本的实现函:ChurnEventGenerator::kick(Observed *o, ObserverInfo *oi)作相应的处理。

ChurnEventGenerator处理三种基本的事类型:joincrashlookup

 

最后分析一下ChurnEventGenerator::run()

事件发生器,整个模拟都是通这是里发出的事件来推动 join/depatrture/die,继承自 Threaded 的虚函数,以线程方式运行(包装好的底层task)通过预先随机计算所有结点的joinlookup 事件,将其加入到EventQueue中,crash事件是在lookup失效(指定失效概率)后再加到EventQueue的。在计算完成后,才调用EventQueue::Instance()->go();启动

  1. void
  2. ChurnEventGenerator::run()
  3. {
  4. ...
  5.   vector<IPAddress> *_ips = Network::Instance()->getallfirstips();
  6.   IPAddress ip = 0;
  7.   for(u_int xxx = 0; xxx < _ips->size(); xxx++){
  8.     ip = (*_ips)[xxx];
  9.     Args *a = New Args();
  10.     (*a)["wellknown"] = _wkn_string;        //每个结点都要设置引导结点
  11.     (*a)["first"] = _wkn_string; //a hack   //first lookup node
  12.     u_int jointime;
  13.   ...
  14.     if( now() + jointime < _exittime ) {
  15.         P2PEvent *e = New P2PEvent(now() + jointime, ip, "join", a);    //kejie:生成单个P2P事件
  16.         add_event(e);       //加入事件队列
  17.     } else {
  18.       delete a;
  19.       a = NULL;
  20.     }//end if
  21. ...
  22.     if( _lookupmean > 0 && now() + jointime + tolookup < _exittime ) {
  23.       P2PEvent *e = New P2PEvent(now() + jointime + tolookup, ip, "lookup", a); //kejie:查找事件
  24.       add_event(e);
  25.     } else {
  26.       delete a;
  27.     }//end if
  28.   }//end for

EventQueue::Instance()->go();        //kejie:确定好所有结点的加入/查找事件时间,就可以启动事件运行.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值