zoukankan      html  css  js  c++  java
  • (转)关于后台加载队列的设计

    如有转载请注明:
    http://www.azure.com.cn/

    在游戏中,把部分的加载和合成工作,放到后台(多线程)中,是提高游戏体验,加快游戏速度的重要手段之一, 但线程的使用,却产生了很复杂的异步操作, 从此会造成空间上和时间上的,对资源,特别是独占资源的不可控制性, 一旦引入了多线程, 将大大提高了程序的出错概率和调试难度. 但是如果通过合理的设计, 有效的调度, 还是可以避免大部分的问题的.
    在这里不是介绍如何创建一个线程, 这些都是很EASY的事情, 关键是要通过一种有效的设计方式, 使其安全稳定的运作起来.
    下面有这样一个景像: 当你的玩家在某个地方上线以后, 这时你周围有非常多的玩家, 这是服务器会发很多创建角色的消息到你的客户端, 将你周围所有的玩家,怪物,包括你自己创建出来, 单个创建的过程, 分析下来大概包括,以下步骤:
    • 1. 创建模型
    • 2. 加载动画
    • 3. 加载纹理
    • 4. 合成纹理
    • 5. 加载装备
    • 6. 绑定装备
    所以说,创建一个角色的消耗还是比较大的, 当有数百个玩家需要同时创建的时候, 如果没有任何缓冲策略的话, 轻则游戏卡住个几分钟,无反映, 重则不稳定, 程序直接就挂掉了. 就算是轻的, 我想也没有也没有那个玩家愿意卡在那里几分钟而没有反映吧!
    所以,我们需要一个后台加载队列!
    我们将需要处理的消息(Background Message)添加到队位, 而有一后台线程, 不停地从对首取消息, 进行处理. 我们将后台线程看做一个"永远不会停歇的工人", 只是在队列为空的时候才稍微打盹一下.他的大致的处理过程如下:
    void thread()
    {
     for(; ; ;)
     {
     bool rest = isBackgroundQueueEmpty(); //后台队列空了吗?
     if(rest)
     {
     sleep(2000);
     continue;
     }
     else
     sleep(100);

     ..... 加载! 加载 !
     [MARK A]
     }

    }
    由上伪代码可见, 此工人只有在后台队列为空时,能休息一下,但他却总不能完全停歇, 因为此线程的生命周期与整个游戏一样长.
    此线程不只只限制处理一种类型的消息, 它应该能处理各种消息, 就算了一些毫无关联性的消息, 比如换装消息, 和删除消息. 当然这使我们马上想到继承.

    先看下面一张图:


    根类是一个BgMessage, 他只有一个接口就是getType(), 其实我们不能直接操纵它做任何事情, 它并不含有任何数据, 我们需要的是派生类, 但它可以让我们把所有的不同消息通过std::queue 来串起来.
    我们这里只列举了三个消息, 创建, 删除, 和换装.
    比如当我们收到创建消息以后, 可以
    BgMessage* bgmsg = new CreateMessage();
    bgmsg->setUid(xxxx);
    bgmsg->setGender(xxxxx);
    bgmsg->setRace(xxxx);
    .........
    queue.push_back(bgmsg);

    还没有说getType()是什么意思呢, 它是返回类的标志, 标志此类是什么类.
    所以上面[MARK A]的代码处,我们这样分析:
    BgMessge* msg = queue.pop_front();
    if(msg->getType() == CREATE_MESSAGE)
     doCreateMsg( static_cast<CreateMessage*>(msg) );
    if(msg->getType() == DELETE_MESSAGE)
     doDeleteMsg( static_cast<DeleteMessage*>(msg) );
    if(msg->getType() == EQUIP_MESSAGE)
     doEquipMsg( static_cast<EquipMessage*>(msg) );
    delete msg;
    这样抽象的消息就被具体化,
    并给予不同的处理单元来处理了.
    由于msg对象是开始被new出来的, 所以在消息被队首弹出来后, 需要手工delete掉.

    还有一点需要注意的是, 每种不同的消息, 都是对收到此消息当前现场情况的一种封装, 所以需要使用值拷贝来把现场复制下来, 切不可直接用个指针来赋值, 因为你不知道你真正指的地方是个什么东西, 有可能只是个临时变量, 比如创建角色的名称, 以前错误的用一个指针指, 由于你真正处理到这条消息的时候, 那个字符串不知道早就被谁给delete掉了.

    www.azure.com.cn
  • 相关阅读:
    set RowCount 与 top n
    Ubuntu 12.04 64bit 安装编译GCC 4.1.2 绝对原创
    C语言第11课
    Cocos2d-x之MenuItem
    wamp 已安装cakephp Fatal error: You must enable the intl extension to use CakePHP. in XXX
    jQuery性能优化38建议---最引人注目的用户体验!
    在面对变化,撇开NO
    批学习
    JAVA修饰符类型(public,protected,private,friendly)
    【Espruino】NO.15 nRF24L01+无线收发器
  • 原文地址:https://www.cnblogs.com/lancidie/p/1846491.html
Copyright © 2011-2022 走看看