silverlight游戏设计(二)资源管理篇(上)--实现一个多任务资源下载器

本文介绍了一种用于Silverlight游戏的下载器设计方法,该下载器能够在游戏运行时动态加载资源,通过限制WebClient数量并池化来提高下载效率,并采用回调机制通知下载结果。

我在silverlight游戏设计(一)主程序加载器这篇文章中写的那个加载器中其实已经包含了一个下载器。不过本篇需要的是一个在游戏运行时动态执行的下载器,这两者有什么区别呢?一方面在于前篇的那个下载器是串行下载所有文件,而对于一个正在运行的网游,因为我们对大部分资源(比如图片、脚本等)采取动态加载的策略,所以它随时都有可能去请求下载,也就是需要同时进行更多的下载任务;二是我们这个下载器与接下来资源管理服务时紧密相关,所以需要重新设计新的接口。

设计要点 

image

a.限制webclient数量,池化webclient

带宽总是有限的,我们不可能每次需要下载一个资源时都new一个webclient,最好的方式是我们可以通过配置限定最多允许多少个webclient执行下载任务,多出这个数量的下载请求先辈暂存起来,当其他下载任务结束时再进行下载。

关于这个功能的实现,我们可以先判断当前工作中的webclient数量,如果大于限制数则将任务请求存放到队列中。

ExpandedBlockStart.gif 代码片段-Download方法
public   void  Download(Uri uri, DownloadResultEventHandler callback)

        {

            
if (_currentWorkersNum  +   1   >  _maxConnectionInPool)

            {

                enqueue(
new  InnerBuffData() { Uri  =  uri, Handler  =  callback });

                
return ;

            }

            var client 
=  _webClients.Detach();

            client.OpenReadCompleted 
+=  onDownloadResultArrive;

            client.OpenReadAsync(uri, callback);

            increaseRefCounter();

        }

 

 

在一个任务结束时,检查当前任务队列是否有剩余任务,如果有提取出来继续下载,否则将工作中的webclient计数减1。这里要注意加锁。

ExpandedBlockStart.gif 代码片段 - onDownloadResultArrive
private   void  onDownloadResultArrive( object  sender, OpenReadCompletedEventArgs args)

        {
 …… …… ……
 
lock  (_lock_BuffTargets)

            {

                
if  (_buffTargets.Count  !=   0 )

                {

                    var targetInfo 
=  dequeue();

                    client.OpenReadAsync(targetInfo.Uri, targetInfo.Handler);

                }

                
else

                {

                    client.OpenReadCompleted 
-=  onDownloadResultArrive;

                    decreaseRefCounter();

                    _webClients.Attach(client);

                    
return ;

                }

            }
        }

 

 

b.在下载结束(失败或是成功)后通过回调的方式进行通知。所以一个任务除了包含URI之外,还需要一个回调对象。这里我定义了一个委托类型:

public delegate void DownloadResultEventHandler(MemoryStream stream, DownloadStatus status);
能够将数据流和下载状态(完成/失败等)数据进行传递。
DownloadDelegater代码如下(详细见源码中的
Sopaco.Silverlight.Insfrastructure.Net.DownloadActivator
这个命名空间):

 
ExpandedBlockStart.gif DownloadDelegater
namespace  Sopaco.Silverlight.Insfrastructure.Net.DownloadActivator
{
    
public   class  DownloadDelegater : IDisposable
    {
        
class  InnerBuffData
        {
            
public  Uri Uri
            {
                
get ;
                
set ;
            }
            
public  DownloadResultEventHandler Handler
            {
                
get ;
                
set ;
            }
        }

        
#region  Constaints
        
private   readonly   static   int  DEFAULT_CONNECTION_IN_POOL  =   5 ;
        
#endregion
        
#region  Fields
        
///   <summary>
        
///  池化WebClient
        
///   </summary>
         private  IGenericPool < WebClient >  _webClients  =   new  GenericPool < WebClient > ();
        
///   <summary>
        
///  缓冲,当WebClient数量达到限制时将需要等待的资源Uri放到这里。
        
///   </summary>
         private  Queue < InnerBuffData >  _buffTargets  =   new  Queue < InnerBuffData > ();
        
///   <summary>
        
///  最大启用的WebClient数量
        
///   </summary>
         private   int  _maxConnectionInPool  =  DEFAULT_CONNECTION_IN_POOL;
        
///   <summary>
        
///  当前进行中的WebClient数量
        
///   </summary>
         private   int  _currentWorkersNum  =   0 ;
        
#endregion

        
#region  lockers
        
private   object  _lock_CurrentWorkersNum  =   new   object ();
        
private   object  _lock_BuffTargets  =   new   object ();
        
#endregion


        
#region  ctors
        
public  DownloadDelegater()
        {
            
        }
        
#endregion
        
#region  Properties
        
public   int  MaxConnectionInPool
        {
            
get
            {
                
return  _maxConnectionInPool;
            }
            
set
            {
                _maxConnectionInPool 
=  value;
            }
        }
        
#endregion
        
public   void  Download(Uri uri, DownloadResultEventHandler callback)
        {
            
if (_currentWorkersNum  +   1   >  _maxConnectionInPool)
            {
                enqueue(
new  InnerBuffData() { Uri  =  uri, Handler  =  callback });
                
return ;
            }
            var client 
=  _webClients.Detach();
            client.OpenReadCompleted 
+=  onDownloadResultArrive;
            client.OpenReadAsync(uri, callback);
            increaseRefCounter();
        }

        
#region  IDisposable Members

        
public   void  Dispose()
        {
            disposeWebclientPool();
            _webClients.Dispose();
        }

        
#endregion
        
#region  Private Helper Methods
        
private   void  disposeWebclientPool()
        {
            _webClients.Compact(
0 );
        }
        
private   void  onDownloadResultArrive( object  sender, OpenReadCompletedEventArgs args)
        {
            var callback 
=  args.UserState  as  DownloadResultEventHandler;
            var client 
=  sender  as  WebClient;
            
if (args.Result  !=   null   &&  args.Error  ==   null   &&   ! args.Cancelled)
            {
                var memStream 
=   new  MemoryStream();
                args.Result.CopyTo(memStream);
                memStream.ResetPosition();
                callback(memStream, DownloadStatus.Completed);
            }
            
else
            {
                
if (args.Cancelled)
                {
                    callback(
null , DownloadStatus.Canceled);
                }
                
else
                {
                    callback(
null , DownloadStatus.Error);
                }
            }
            
lock  (_lock_BuffTargets)
            {
                
if  (_buffTargets.Count  !=   0 )
                {
                    var targetInfo 
=  dequeue();
                    client.OpenReadAsync(targetInfo.Uri, targetInfo.Handler);
                }
                
else
                {
                    client.OpenReadCompleted 
-=  onDownloadResultArrive;
                    decreaseRefCounter();
                    _webClients.Attach(client);
                    
return ;
                }
            }
        }
        
private   void  increaseRefCounter()
        {
            
lock  (_lock_CurrentWorkersNum)
            {
                _currentWorkersNum
++ ;
            }
        }
        
private   void  decreaseRefCounter()
        {
            
lock  (_lock_CurrentWorkersNum)
            {
                _currentWorkersNum
-- ;
            }
        }
        
private   void  enqueue(InnerBuffData data)
        {
            
lock (_lock_BuffTargets)
            {
                _buffTargets.Enqueue(data);
            }
        }
        
private  InnerBuffData dequeue()
        {
            
lock (_lock_BuffTargets)
            {
                
return  _buffTargets.Dequeue();
            }
        }
        
#endregion
    }
}

完整源码下载

转载于:https://www.cnblogs.com/wJiang/archive/2010/12/21/1913192.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值