silverlight游戏设计(二)资源管理篇(上)--实现一个多任务资源下载器
时间:2010-12-21 来源:姜 萌@cnblogs
设计要点
a.限制webclient数量,池化webclient
带宽总是有限的,我们不可能每次需要下载一个资源时都new一个webclient,最好的方式是我们可以通过配置限定最多允许多少个webclient执行下载任务,多出这个数量的下载请求先辈暂存起来,当其他下载任务结束时再进行下载。
关于这个功能的实现,我们可以先判断当前工作中的webclient数量,如果大于限制数则将任务请求存放到队列中。

{
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。这里要注意加锁。

{
…… …… ……
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
这个命名空间):
DownloadDelegaternamespace 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
}
}
完整源码下载
相关阅读 更多 +
排行榜 更多 +