@a604572782
        
        2016-02-04T07:33:10.000000Z
        字数 9678
        阅读 2730
    Async c#
在.net4.0以后异步操作,并行计算变得异常简单,但是由于公司项目开发基于.net3.5所以无法用到4.0的并行计算以及Task等异步编程。因此,为了以后更方便的进行异步方式的开发,我根据.net的使用方式封装了异步编程框架,通过BeginInvoke、EndInvoke的方式实现异步编程。
整个框架包括四个部分 
1. 基类抽象Opeartor 
我把每个异步执行过程称为一个Operate,因此需要一个Opeartor去执行 
2. FuncAsync 
异步的Func 
3. ActionAsync 
异步的Action 
4. Asynchorus 
对ActionAsync和FuncAsync的封装
Operator是一个抽象类,实现了IOperationAsync和IContinueWithAsync两个接口。 
IOperationAsync实现了异步操作,IContinueWithAsync实现了类似于Task的ContinueWith方法,在当前异步操作完成后继续进行的操作
public interface IOperationAsync{IAsyncResult Invoke();void Wait();void CompletedCallBack(IAsyncResult ar);void CatchException(Exception exception);}
Invoke():异步方法的调用Wait():等待异步操作执行CompletedCallBack():操作完成回调CatchException():抓取异常
public interface IContinueWithAsync{Operator Previous { get; set; }Operator Next { get; set; }Operator ContinueWithAsync(Action action);Operator ContinueWithAsync<TParameter>(Action<TParameter> action, TParameter parameter);}
Previous:前一个操作Next:下一个操作ContinueWithAsync():异步继续操作
public abstract class Operator : IOperationAsync, IContinueWithAsync{public IAsyncResult Middle;public readonly string Id;public Exception Exception { get; private set; }public Operator Previous { get; set; }public Operator Next { get; set; }protected Operator(){Id = Guid.NewGuid().ToString();}public abstract IAsyncResult Invoke();protected void SetAsyncResult(IAsyncResult result){this.Middle = result;}public virtual void Wait(){if (!Middle.IsCompleted) Middle.AsyncWaitHandle.WaitOne();}public virtual void CompletedCallBack(IAsyncResult ar){}public void CatchException(Exception exception){this.Exception = exception;}protected Operator ContinueAsync(){if (Next != null) Next.Invoke();return Next;}public virtual Operator ContinueWithAsync(Action action){Next = new ActionAsync(action);Next.Previous = this;return Next;}public virtual Operator ContinueWithAsync<TParameter>(Action<TParameter> action, TParameter parameter){Next = new ActionAsync<TParameter>(action, parameter);Next.Previous = this;return Next;}public virtual Operator ContinueWithAsync<TResult>(Func<TResult> func){Next = new FuncAsync<TResult>();Next.Previous = this;return Next;}public virtual Operator ContinueWithAsync<TParameter, TResult>(Func<TParameter, TResult> func,TParameter parameter){Next = new FuncAsync<TParameter, TResult>(func, parameter);Next.Previous = this;return Next;}}
public class ActionAsync : Operator{private readonly Action _action;protected ActionAsync(){}public ActionAsync(Action action): this(){this._action = action;}public override IAsyncResult Invoke(){var middle = _action.BeginInvoke(CompletedCallBack, null);SetAsyncResult(middle);return middle;}public override void CompletedCallBack(IAsyncResult ar){try{_action.EndInvoke(ar);}catch (Exception exception){this.CatchException(exception);}ContinueAsync();}}public class ActionAsync<T> : ActionAsync{public T Result;private readonly Action<T> _action1;protected readonly T Parameter1;public ActionAsync(){}public ActionAsync(T parameter){this.Parameter1 = parameter;}public ActionAsync(Action<T> action, T parameter){this._action1 = action;this.Parameter1 = parameter;}public override IAsyncResult Invoke(){var result = _action1.BeginInvoke(Parameter1, CompletedCallBack, null);SetAsyncResult(result);return result;}public override void CompletedCallBack(IAsyncResult ar){try{_action1.EndInvoke(ar);}catch (Exception exception){this.CatchException(exception);}ContinueAsync();}}
FuncAsync实现了IFuncOperationAsync接口
public interface IFuncOperationAsync<T>{void SetResult(T result);T GetResult();}
SetResult(T result):异步操作完成设置返回值GetResult():获取返回值
public class FuncAsync<TResult> : Operator, IFuncOperationAsync<TResult>{private TResult _result;public TResult Result{get{if (!Middle.IsCompleted || _result == null){_result = GetResult();}return _result;}}private readonly Func<TResult> _func1;public FuncAsync(){}public FuncAsync(Func<TResult> func){this._func1 = func;}public override IAsyncResult Invoke(){var result = _func1.BeginInvoke(CompletedCallBack, null);SetAsyncResult(result);return result;}public override void CompletedCallBack(IAsyncResult ar){try{var result = _func1.EndInvoke(ar);SetResult(result);}catch (Exception exception){this.CatchException(exception);SetResult(default(TResult));}ContinueAsync();}public virtual TResult GetResult(){Wait();return this._result;}public void SetResult(TResult result){_result = result;}}public class FuncAsync<T1, TResult> : FuncAsync<TResult>{protected readonly T1 Parameter1;private readonly Func<T1, TResult> _func2;public FuncAsync(Func<T1, TResult> action, T1 parameter1): this(parameter1){this._func2 = action;}protected FuncAsync(T1 parameter1): base(){this.Parameter1 = parameter1;}public override IAsyncResult Invoke(){var result = _func2.BeginInvoke(Parameter1, CompletedCallBack, null);SetAsyncResult(result);return result;}public override void CompletedCallBack(IAsyncResult ar){try{var result = _func2.EndInvoke(ar);SetResult(result);}catch (Exception exception){CatchException(exception);SetResult(default(TResult));}ContinueAsync();}}
ActionAsync和FuncAsync为异步操作打下了基础,接下来最重要的工作就是通过这两个类执行我们的异步操作,为此我封装了一个异步操作类 
主要封装了以下几个部分: 
1. WaitAll(IEnumerable<Operator> operations):等待所有操作执行完毕 
2. WaitAny(IEnumerable<Operator> operations):等待任意操作执行完毕 
3. ActionAsync 
4. FuncAsync 
5. ContinueWithAction 
6. ContinueWithFunc
后面四个包含若干个重载,这里只是笼统的代表一个类型的方法
public static void WaitAll(IEnumerable<Operator> operations){foreach (var @operator in operations){@operator.Wait();}}
public static void WaitAny(IEnumerable<Operator> operations){while (operations.All(o => !o.Middle.IsCompleted))Thread.Sleep(100);}
等待时间可以自定义
public static Operator Invoke(Action action){Operator operation = new ActionAsync(action);operation.Invoke();return operation;}public static Operator Invoke<T>(Action<T> action, T parameter){Operator operation = new ActionAsync<T>(action, parameter);operation.Invoke();return operation;}public static Operator Invoke<T1, T2>(Action<T1, T2> action, T1 parameter1, T2 parameter2){Operator operation = new ActionAsync<T1, T2>(action, parameter1, parameter2);operation.Invoke();return operation;}
public static Operator Invoke<TResult>(Func<TResult> func){Operator operation = new FuncAsync<TResult>(func);operation.Invoke();return operation;}public static Operator Invoke<TParameter, TResult>(Func<TParameter, TResult> func, TParameter parameter){TParameter param = parameter;Operator operation = new FuncAsync<TParameter, TResult>(func, param);operation.Invoke();return operation;}public static Operator Invoke<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 parameter1, T2 parameter2){Operator operation = new FuncAsync<T1, T2, TResult>(func, parameter1, parameter2);operation.Invoke();return operation;}
public static Operator ContinueWithAsync(IEnumerable<Operator>operators, Action action){return Invoke(WaitAll, operators).ContinueWithAsync(action);}public static Operator ContinueWithAsync<TParameter>(IEnumerable<Operator> operators, Action<TParameter> action, TParameter parameter){return Invoke(WaitAll, operators).ContinueWithAsync(action, parameter);}
public static Operator ContinueWithAsync<TResult>(IEnumerable<Operator> operators,Func<TResult> func){return Invoke(WaitAll, operators).ContinueWithAsync(func);}public static Operator ContinueWithAsync<TParameter, TResult>(IEnumerable<Operator> operators,Func<TParameter, TResult> func, TParameter parameter){return Invoke(WaitAll, operators).ContinueWithAsync(func, parameter);}
这里有个bug当调用
ContinueWithAsync后无法调用Wait等待,本来Wait需要从前往后等待每个异步操作,但是测试了下不符合预期结果。不过理论上来说应该无需这样操作,ContinueWithAsync只是为了当上一个异步操作执行完毕时继续执行的异步操作,若要等待,那不如两个操作放到一起,最后再等待依然可以实现。
前面的都是单步异步操作的调用,若需要对某集合进行某个方法的异步操作,可以foreach遍历
public void ForeachAsync(IEnumerbale<string> parameters){foreach(string p in parameters){Asynchronous.Invoke(Tast,p);}}public void Test(string parameter){//TODO:做一些事}
每次都需要去手写foreach,比较麻烦,因此实现类似于PLinq的并行计算方法实在有必要,不过有一点差别,PLinq是采用多核CPU进行并行计算,而我封装的仅仅遍历集合进行异步操作而已
public static IEnumerable<Operator> Foreach<TParameter>(IEnumerable<TParameter> items, Action<TParameter> action){return items.Select(t => Invoke(action, t)).ToList();}
public static IEnumerable<Operator> Foreach<TParameter, TResult>(IEnumerable<TParameter> items, Func<TParameter, TResult> func){return items.Select(parameter => Invoke(func, parameter)).ToList();}
public void DoSomeThing(){//TODO:}
通过Asynchronous.Invoke(DoSomeThing) 执行
public void DoSomeThing(string parameter){//TODO:}
通过Asynchronous.Invoke(DoSomeThing,parameter) 执行
public string DoSomeThing(){//TODO:}
通过Asynchronous.Invoke(()=>DoSomeThing())执行
public string DoSomeThing(string parameter){//TODO:}
通过Asynchronous.Invoke(()=>DoSomeThing(parameter))执行,或者也可以传入参数通过Asynchronous.Invoke(p=>DoSomeThing(p),parameter)
public void Test{int[] parameters = {1,2,3,4,5};Asynchronous.Foreach(parameters,Console.WriteLine);}
public void Test{int[] parameters = {1,2,3,4,5};var operators = Asynchronous.Foreach(parameters,p=> p*2);Asynchrous.WaitAll(operators);Asynchronous.Foreach(operators.Cast<FuncAsync<int,int>>(),p=> Console.WriteLine(p.Result));}
首先将集合每个值扩大2倍,然后输出
public void Test{int[] parameters = {1,2,3,4,5};var operators = Asynchronous.Foreach(parameters,p=> p*2);Asynchrous.ContinueWithAsync(operators,Console.WriteLine,"执行完成");}
public void Test{int[] parameters = {1,2,3,4,5};var operators = Asynchronous.Foreach(parameters,p=> p*2);Asynchronous.Foreach(operators,o=>{o.ContinueWithAsync(()={//每个元素执行完时执行if(o.Exception != null){//之前执行时产生未处理的异常,这里可以捕获到}});});}
public void Chain(){Asynchronous.Invoke(Console.WriteLine,1).ContinueWithAsync(Console.WriteLine,2).ContinueWithAsync(Console.WriteLine,3)}
这样会按步骤输出1,2,3
以上只是列出了部分重载方法,其他重载方法无非就是加参数,本质实际是一样的,具体可以下载源码。我还封装了for方法,但是感觉没什么用,而且也从没有用到过,这里不再提
通过以上的封装,已经能完成日常大部分的操作,调用还是比较方便的。
本文发布至作业部落