Ascent代码分析2-底层shared

底层shared工程

1.     线程&线程池

 

 

 

线程对象的基类ThreadBase提供了2个接口:

  1. virtual bool run() = 0;
  2. virtual void OnShutdown() {};

新建线程对象只需要从ThreadBase派生,并实现run接口。然后使用CThreadPool::ExecuteTask()将线程对象加入线程池中,线程池内部配一个空闲线程(如果没有就新建一个)去执行该对象的run函数。

ThreadBase有点类似ACE中的主动对象ACE_Task,不同的是ThreadBase本身没有线程,而是使用的线程池CThreadPool的线程。CThreadPool::IntegrityCheck以类似回调函数的形式供上层调用,释放或新加空闲线程来保证线程数量稳定在一个常数范围内。

注:实际上作为上层基类的类是从ThreadBase派生的CThread,封装了一些状态控制的操作。

 

2.     网络

Socket

封装了SOCKET套接字的操作及读写同步和缓冲。其中缓冲区使用了一个封装的环形缓冲区CircularBuffer。使得上层应用处理TCP连接像发送/接收操作没有一次完成这种情况变得很简单。发送之后如果一次没有发完会在工作线程中接着发送(HandleWriteComplete中调用WriteCallback),而上层的接收操作如果发现数据没有接收完全,可以直接返回(工作线程会再投递一个recv操作),然后等待下次recv调用完成,直至接收数据包完成(可参看任何一个Socket派生类的OnRead)。

但是在Ascent中,消息处理都放在了OnRead中,这个还有改进的余地。更好的做法是当OnRead解析出一个完整的消息包之后,把拆出的应用数据交给上层应用的消息队列中,由应用处理线程去解析应用协议,而不是在通讯的工作线程中去处理。这样可以避免应用处理阻塞时整个通讯线程也跟着阻塞。

 

SocketMgr

SocketMgr是一个单体对象(基本上Mgr都是从Singleton派生的),负责管理所有的Socket对象,SocketMgr在构造函数中会创建一个完成端口,并在线程池CThreadPool中添加一定数量的工作线程(SocketWorkerThread.SocketWorkerThread将从完成端口队列中取出完成操作,根据不同的操作调用相应的处理函数:

  1. if(ov->m_event < NUM_SOCKET_IO_EVENTS)
  2.      ophandlers[ov->m_event](s, len);

这里使用了一种消息处理的方式,曾经见过一种说法叫跳跃表,但是也不太确定。就是将多个函数指针放入一个函数指针数组中,消息号(协议号)则作为函数指针数组的下标,从而可以直接调用相应的处理函数,而不必写多个case语句。其他很多地方,比如消息处理,也都是使用的这种方法。

在处理函数中会调用相应Socket类的虚函数接口(如OnRead)。通讯类只需要从Socket类派生并实现虚函数接口即可。 释放的socket会放入SocketGarbageCollector的队列中在update时删除

ListenSocket

ListenSocket是一个模板类,其参数TSocket类(或者其的派生类)。ListenSocket是从ThreadBase派生(而不是Socket类)。ListenSocket在构造函数中绑定一个监听端口,并在run()函数中接受连接,使用AcceptSOCKET(大写)初始化一个T类型的对象,然后调用T::Accept()绑定到完成端口(AssignToCompletionPort)并在完成端口投递一个recv操作(SetupReadEvent),然后加入到SocketMgrSinglton对象中。

 

3.     数据库操作:

 

这里使用了一个 Factor Method模式,通过CreateDatabaseInterface接口,使用配置参数创建出一个子类对象,并返回一个Database指针。上层应用中只能使用Database的接口,从而可以将不同的数据库实现封装起来,并可以根据配置文件调整数据库类型。

这里提供了SQLiteMySQLPostgres三种数据库的实现。

DatabaseCThread派生,并使用了数据库连接池,除了提供同步的查询外,还提供了:

异步执行sql。添加的sql语句可以加入队列中queries_queue,在Databaserun函数中将以阻塞的方式从队列中取出查询语句执行。

异步的事务提交。批量sql语句以QueryBuffer的形式放在另一个队列query_buffer中。类class QueryThread : public CThreadrun函数将调用Database::thread_proc_query函数,以事务的方式批量提交。

(这里描述起来很复杂,一看代码应该就明白了吧..

其他:

Vmap:这个是矢量地图。还有g3dlite好像是最近才加进来的。用于服务器端的地形判断,解决一些类似法师闪现穿墙的问题。这方面不熟,忽略之.

再其他就是一些工具性质的代码。其中CrashHandler.cpp中有关于程序崩溃后的内存转储的实现(生成.dmp文件)。能够通过打开在程序崩溃后产生的.dmp文件,直接定位到崩溃代码,找bug比较有用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 双向升华算法(Dual Ascent)是一种用于解决优化问题的算法,其时间步(time step)是指算法在每一轮迭代中的操作。在每一个时间步中,双向升华算法通过分别更新原始变量和对偶变量来逐步靠近最优解。 双向升华算法的时间步包括以下几个关键步骤: 1. 初始化:在第一个时间步中,将原始变量和对偶变量初始化为某个初始值。 2. 更新原始变量:根据当前的对偶变量值,通过最小化原始问题来更新原始变量。这个更新步骤使用了对偶函数的负梯度方向,并根据问题的特性进行调整。 3. 更新对偶变量:根据更新后的原始变量值,通过最大化对偶问题来更新对偶变量。这个更新步骤使用了原始问题的梯度方向,并根据问题的特性进行调整。 4. 终止判断:在每个时间步的最后,检查算法是否已经达到了停止条件。停止条件可以是一定的迭代次数、目标函数变化量或对偶变量的变化量等。 5. 更新时间步:如果算法未终止,则将时间步加一,并回到步骤2。 双向升华算法通过不断迭代原始变量和对偶变量的更新来逐渐优化问题,每个时间步都在向最优解靠近。时间步的设置取决于问题的复杂性和收敛速度的要求。较小的时间步可能导致算法收敛速度慢,而较大的时间步可能导致算法在求解过程中发散。 总之,双向升华算法的时间步是指算法在每一轮迭代中的操作,包括原始变量和对偶变量的更新,以及终止条件的判断。通过合适的时间步设置,双向升华算法可以高效地求解优化问题。 ### 回答2: 对于双重上升算法(dual ascent),time-step是指每次更新对偶变量(dual variables)的时间间隔。虽然时间步骤的大小可以根据特定问题进行调整,但通常是以离散步幅(discrete increments)的形式进行。比如,在线性规划问题中,时间步骤通常是一个既定的常数。 使用较小的时间步长进行双重上升更新可能会导致算法的收敛速度较慢,因为解可能在更新之间改变较小。相反,更大的时间步长可能会导致算法在解空间中产生更大的波动,可能会导致不稳定的收敛性或困难的更新。 因此,选择适当的时间步长很重要,以平衡收敛速度和数值稳定性。通常,需要根据问题的特性进行实验,尝试不同的时间步骤大小以找到最佳的收敛速度和数值稳定性的平衡点。 ### 回答3: 对于双重上升法,time-step (时间步长)指的是在每次迭代中更新变量值的幅度。在双重上升法中,我们通过最大化某个目标函数来优化问题,可以将其分解为两个子问题:主问题和对偶问题。 在每次迭代中,主问题通过选择一个变量来最大化目标函数,并将其改变一个小的幅度。这个幅度就是时间步长。时间步长的选择可以根据问题的特性来确定,一般来说,我们希望时间步长足够小,以便在每次迭代中能够找到更接近最优解的变量值。 然而,时间步长不能太小,否则优化过程可能会变得非常缓慢。因此,在选择时间步长时需要权衡速度和精确度,寻找一个合理的折衷。 在对偶问题中,时间步长的选择也很重要。对偶问题通过选择一个变量来最小化目标函数,并将其改变一个小的幅度。与主问题类似,时间步长的选择取决于问题的特性和求解速度要求。 总而言之,时间步长在双重上升法中起到控制变量更新幅度的作用。合适的时间步长选择可以在迭代过程中平衡速度和精确度,从而更好地优化目标函数。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值