代码改变世界

异步文件IO的应用

2007-10-30 00:05  莫耶  阅读(628)  评论(1编辑  收藏  举报
  最近项目中大量运用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, nullfalse, 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]);
                    
采用回调方式的做法
                    
不采用回调方式时的做法
                }

            }


        }

        
void callback(IAsyncResult ar)
        
{

            AsyncDataAccessState state 
= (AsyncDataAccessState)ar.AsyncState;
            WorkerEventHander weh 
= state.Worker;
            weh.EndInvoke(ar);
            ReadLargeFileCompletedEventArgs args 
= new ReadLargeFileCompletedEventArgs(state.FileContent, nullfalse, 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(
                        
nullnulltrue, 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, 0x1000true);
            
byte[] buffer = new byte[0x1000];
            
int res = fs.Read(buffer, 00x1000);
            
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点是互斥的
                res 
= fs.Read(buffer, 00x1000);
            }

            fs.Close();
            
不采用回调方式时的做法


        }

        
/// <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()

    
// ---------------------------  异步事件 ------------------------------- //
    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();
        }

    }