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
  • 相关阅读:
    BZOJ 1391: [Ceoi2008]order
    BZOJ 4504: K个串
    2019 年百度之星·程序设计大赛
    POJ 2398 Toy Storage (二分 叉积)
    POJ 2318 TOYS (二分 叉积)
    HDU 6697 Closest Pair of Segments (计算几何 暴力)
    HDU 6695 Welcome Party (贪心)
    HDU 6693 Valentine's Day (概率)
    HDU 6590 Code (判断凸包相交)
    POJ 3805 Separate Points (判断凸包相交)
  • 原文地址:https://www.cnblogs.com/lancidie/p/1846491.html
Copyright © 2011-2022 走看看