最近项目中大量运用xml和静态页来做缓存碎片,好处嘛有二:
1、文件碎片的过期比较灵活,不由计划任务调度,而是由访客决定何时过期,这样冷门页面自然就不用劳系统的神进行更新;
2、较之静态页,采用缓存文件碎片+动态页方式,能更加灵活地实现功能的控制,诸如一些小功能的更改。
但经过压力测试,发现同步的文件I/O,在并发过大的情况下,经常会出现锁文件的异常,颇让人头疼
只有借助异步I/O来解决了,在网上找来了一个功能较完备的工具集代码族,如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.IO;

namespace IndieFacade


{

/**//// <summary>
/// 异步完成事件
/// </summary>
public class ReadLargeFileCompletedEventArgs : AsyncCompletedEventArgs

{
byte[] _buffer;
public byte[] Buffer

{

get
{ return _buffer; }
}
public ReadLargeFileCompletedEventArgs(byte[] buffer, Exception e, bool canncel, object state)
: base(e, canncel, state)

{
_buffer = buffer;
}
}

/**//// <summary>
/// 异步执行进度
/// </summary>
public class ReadLargeFileProgressEventArgs : ProgressChangedEventArgs

{
int _FileLen;
int _Reads;
byte[] buffer;
public ReadLargeFileProgressEventArgs(int iFileLength, int iHasRead, int Percentage, object state, byte[] _Buffer)
:
base(Percentage, state)

{
_FileLen = iFileLength;
_Reads = iHasRead;
buffer = _Buffer;

}
public int FileLength

{

get
{ return _FileLen; }
}
public int HasRead

{

get
{ return _Reads; }
}
public byte[] Buffer

{

get
{ return buffer; }
}
}

/**//// <summary>
/// 读取完成完成
/// </summary>
public delegate void ReadLargeFileCompletedEventHander(object sender, ReadLargeFileCompletedEventArgs e);

/**//// <summary>
/// 进度改变
/// </summary>
public delegate void ReadLargeFileProgressEventHandler(object sender, ReadLargeFileProgressEventArgs e);


/**//// <summary>
/// 异步数据访问
/// </summary>
public class AsyncDataAccess

{
internal delegate void WorkerEventHander(string sFilePath, object userToken);

public event ReadLargeFileCompletedEventHander ReadCompleted;
public event ReadLargeFileProgressEventHandler ReadProgressChanged;

HybridDictionary taskToker = new HybridDictionary();

SendOrPostCallback _complated;
SendOrPostCallback _reportProgress;
SendOrPostCallback _completionMethod;
protected void OnReadCompleted(ReadLargeFileCompletedEventArgs args)

{
if (ReadCompleted != null)
ReadCompleted(this, args);
}
protected void OnReadProgressChanged(ReadLargeFileProgressEventArgs args)

{
if (ReadProgressChanged != null)
ReadProgressChanged(this, args);
}

public AsyncDataAccess()

{
_complated = new SendOrPostCallback(comleted);
_reportProgress = new SendOrPostCallback(report);
_completionMethod = new SendOrPostCallback(completionMethod);
}
void comleted(object state)

{
ReadLargeFileCompletedEventArgs args = state as ReadLargeFileCompletedEventArgs;
OnReadCompleted(args);

}
void report(object state)

{
ReadLargeFileProgressEventArgs args = state as ReadLargeFileProgressEventArgs;
OnReadProgressChanged(args);
}

void completionMethod(object state)

{

AsyncDataAccessState ad = (AsyncDataAccessState)state;
AsyncOperation asyncOp = ad.AsyncOp;

ReadLargeFileCompletedEventArgs a = new ReadLargeFileCompletedEventArgs(ad.FileContent, null, false, ad.TaskID);

lock (taskToker.SyncRoot)

{
taskToker.Remove(asyncOp.UserSuppliedState);
}
asyncOp.PostOperationCompleted(_complated, a);
}

public void ReadLargeFile(string sFilePath, long iTaskID)

{
WorkerEventHander weh = new WorkerEventHander(read);
AsyncOperation op = AsyncOperationManager.CreateOperation(iTaskID);
lock (taskToker.SyncRoot)

{
if (!taskToker.Contains(iTaskID))

{
taskToker.Add(iTaskID, op);

FileInfo fi = new FileInfo(sFilePath);
AsyncDataAccessState dast = new AsyncDataAccessState(op, iTaskID, (int)fi.Length, weh, new byte[(int)fi.Length]);

采用回调方式的做法#region 采用回调方式的做法
weh.BeginInvoke(sFilePath, dast, new AsyncCallback(callback), dast);
#endregion

不采用回调方式时的做法#region 不采用回调方式时的做法
//weh.BeginInvoke(sFilePath, dast, null, null);
#endregion
}
}

}
void callback(IAsyncResult ar)

{

AsyncDataAccessState state = (AsyncDataAccessState)ar.AsyncState;
WorkerEventHander weh = state.Worker;
weh.EndInvoke(ar);
ReadLargeFileCompletedEventArgs args = new ReadLargeFileCompletedEventArgs(state.FileContent, null, false, state.TaskID);
_complated(args);
}


public void CancelAsync(int taskId)

{
lock (taskToker.SyncRoot)

{
object obj = taskToker[taskId];
if (obj != null)

{
AsyncOperation asyncOp = obj as AsyncOperation;
ReadLargeFileCompletedEventArgs args = new ReadLargeFileCompletedEventArgs(
null, null, true, taskId);
_complated(args);
}
}
}

private void read(string sFilepath, object userToken)

{
AsyncDataAccessState st = (AsyncDataAccessState)userToken;
FileStream fs = new FileStream(sFilepath, FileMode.Open, FileAccess.Read, FileShare.Read, 0x1000, true);
byte[] buffer = new byte[0x1000];
int res = fs.Read(buffer, 0, 0x1000);
int cnt = 0;
while (res > 0)

{
cnt += res;
byte[] cf = new byte[res];
for (int i = 0; i < res; i++)
cf[i] = buffer[i];
//lock (st.FileContent.SyncRoot)
//{
cf.CopyTo(st.FileContent, 0);
//}
int pctn = (int)((float)cnt / (float)st.FileLen * 100);

ReadLargeFileProgressEventArgs args = new ReadLargeFileProgressEventArgs(st.FileLen, cnt, pctn, st.TaskID, cf);
//Console.WriteLine("read" + cnt.ToString());
this._reportProgress(args);//-----------A

异步发送ChangedEvent,不保证发送的顺序,与A点是互斥的#region 异步发送ChangedEvent,不保证发送的顺序,与A点是互斥的
// st.AsyncOp.Post(this._reportProgress, args);
#endregion
res = fs.Read(buffer, 0, 0x1000);
}
fs.Close();

不采用回调方式时的做法#region 不采用回调方式时的做法
//ReadLargeFileCompletedEventArgs args1 = new ReadLargeFileCompletedEventArgs(st.FileContent, null, false, st.TaskID);
//_completionMethod(st);
#endregion


}

/**//// <summary>
/// 异步数据访问状态类
/// </summary>
internal class AsyncDataAccessState

{
public AsyncOperation AsyncOp;
public long TaskID;
public int FileLen;
public WorkerEventHander Worker;
public Byte[] FileContent;
public AsyncDataAccessState(AsyncOperation op, long iTaskID, int iFileLen, WorkerEventHander workerDelete, Byte[] _FileContent)

{
FileContent = _FileContent;
AsyncOp = op;
TaskID = iTaskID;
FileLen = iFileLen;
Worker = workerDelete;
}
}
}
}
这里稍进行了一点改动,将标识ID(即userToken)改为了long型,在页面级别应用时,可直接将一个时间Ticks塞入以进行标识,如果这还不够,那就相当寸了,但也不坏,至少能读出东西来
据MSDN的说法,要应用异步IO功能,必须将Page的异步模式开关打开,即在页头这样写
<%@ Page sync="true"...,然后,异步代码族的调用必须在OnPreRender(EventArgs e)事件之前(含)调用,那么就可以这么写:
protected override void OnPreRender(EventArgs e)

{
base.OnPreRender(e);
// 功能处理
if (Request.QueryString["id"] == null)
return;
// 传递的艺人ID
this.artId = Convert.ToInt32(Request.QueryString["id"]);
// 获取信息
GenStaticHtml();
}

// --------------------------- 生成方法 ------------------------------- //


/**//// <summary>
/// 生成静态页
/// </summary>

void GenStaticHtml()#region void GenStaticHtml()
private void GenStaticHtml()

{
string HtmlFile = getFileName();

if (!System.IO.File.Exists(HtmlFile))

{
// 获取基本页面信息
GetInfo();
Response.Filter = new StaticFilter(Response.Filter, HtmlFile);
}
else

{
// 过期检查
DateTime dt = System.IO.File.GetLastWriteTime(HtmlFile);
TimeSpan ts = DateTime.Now - dt;
// 过期则删除再生成
if (ts.TotalHours > IndieFacade.ConfigCache.ArtistTimeout)

{
// 获取基本页面信息
GetInfo();
System.IO.File.Delete(HtmlFile);
Response.Filter = new StaticFilter(Response.Filter, HtmlFile);
}
else

{
// 尝试读取已有文件
// 并输出
// 异步
IndieFacade.AsyncDataAccess da = new AsyncDataAccess();
da.ReadCompleted += new ReadLargeFileCompletedEventHander(da_ReadCompleted);
da.ReadProgressChanged += new ReadLargeFileProgressEventHandler(da_ReadProgressChanged);
tid = DateTime.Now.Ticks;
da.ReadLargeFile(HtmlFile, tid);

}
}
}
#endregion

// --------------------------- 异步事件 ------------------------------- //
protected void da_ReadCompleted(object sender, ReadLargeFileCompletedEventArgs e)

{
long id = (long)e.UserState;
if (id == tid)

{
Response.Write(Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length));
Response.Flush();
Response.End();
}
}