简单实现C#异步操作

 更新时间:2020年6月25日 11:25  点击:2142

在.net4.0以后异步操作,并行计算变得异常简单,但是由于公司项目开发基于.net3.5所以无法用到4.0的并行计算以及Task等异步编程。因此,为了以后更方便的进行异步方式的开发,我封装实现了异步编程框架,通过BeginInvoke、EndInvoke的方式实现异步编程。

一、框架结构

整个框架包括四个部分

1、基类抽象Opeartor
我把每个异步执行过程称为一个Operate,因此需要一个Opeartor去执行
2、FuncAsync
异步的Func
3、ActionAsync
异步的Action
4、Asynchorus
对ActionAsync和FuncAsync的封装

Operator
Operator是一个抽象类,实现了IOperationAsync和IContinueWithAsync两个接口。
IOperationAsync实现了异步操作,IContinueWithAsync实现了类似于Task的ContinueWith方法,在当前异步操作完成后继续进行的操作

IOperationAsync接口详解

public interface IOperationAsync
{
  IAsyncResult Invoke();
  void Wait();
  void CompletedCallBack(IAsyncResult ar);
  void CatchException(Exception exception);
}

  • Invoke():异步方法的调用
  • Wait():等待异步操作执行
  • CompletedCallBack():操作完成回调
  • CatchException():抓取异常

IContinueWithAsync接口详情

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;
  }
}

无返回异步操作
ActionAsync

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接口

IFuncOperationAsync

public interface IFuncOperationAsync<T>
{
  void SetResult(T result);
  T GetResult();
}

  • SetResult(T result):异步操作完成设置返回值
  • GetResult():获取返回值

1)、FuncAsync

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();
}
}

Asynchronous 异步操作封装
ActionAsync和FuncAsync为异步操作打下了基础,接下来最重要的工作就是通过这两个类执行我们的异步操作,为此我封装了一个异步操作类
主要封装了以下几个部分:

  • WaitAll(IEnumerable<Operator> operations):等待所有操作执行完毕
  • WaitAny(IEnumerable<Operator> operations):等待任意操作执行完毕
  • ActionAsync
  • FuncAsync
  • ContinueWithAction
  • ContinueWithFunc

后面四个包含若干个重载,这里只是笼统的代表一个类型的方法
WaitAll

public static void WaitAll(IEnumerable<Operator> operations)
{
foreach (var @operator in operations)
{
  @operator.Wait();
}
}

WaitAny

public static void WaitAny(IEnumerable<Operator> operations)
{
while (operations.All(o => !o.Middle.IsCompleted))
  Thread.Sleep(100);
}

等待时间可以自定义
ActionInvoke

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;
}

FuncInvoke

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;
}

ContinueWithAction

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);
}

ContinueWithFunc

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进行并行计算,而我封装的仅仅遍历集合进行异步操作而已
ForeachAction

public static IEnumerable<Operator> Foreach<TParameter>(IEnumerable<TParameter> items, Action<TParameter> action)
{
  return items.Select(t => Invoke(action, t)).ToList();
}

ForeachFunc

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)

无返回值Foreach

public void Test
{
int[] parameters = {1,2,3,4,5};
Asynchronous.Foreach(parameters,Console.WriteLine);
}

有返回值Foreach

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,"执行完成");
}

每次执行完继续执行
可能有时候我们需要遍历一个集合,每个元素处理完成后我们需要输出XX处理完成

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
结束语

以上只是列出了部分重载方法,其他重载方法无非就是加参数,本质实际是一样的。

希望对大家的学习有所帮助,在这祝大家新年快乐,新的一年大家一起努力。

[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25