这篇文章主要为大家详细介绍了C#简单实现异步操作的方法,通过BeginInvoke、EndInvoke的方式实现异步编程,感兴趣的小伙伴们可以参考一下
正文
简单实现C#异步操作
在.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接口详解
|
1
2
3
4
5
6
7
|
public interface IOperationAsync{ IAsyncResult Invoke(); void Wait(); void CompletedCallBack(IAsyncResult ar); void CatchException(Exception exception);} |
- Invoke():异步方法的调用
- Wait():等待异步操作执行
- CompletedCallBack():操作完成回调
- CatchException():抓取异常
IContinueWithAsync接口详情
|
1
2
3
4
5
6
7
|
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():异步继续操作
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
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
|
1
2
3
4
5
|
public interface IFuncOperationAsync<T>{ void SetResult(T result); T GetResult();} |
- SetResult(T result):异步操作完成设置返回值
- GetResult():获取返回值
1)、FuncAsync
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
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
|
1
2
3
4
5
6
7
|
public static void WaitAll(IEnumerable<Operator> operations){foreach (var @operator in operations){ @operator.Wait();}} |
WaitAny
|
1
2
3
4
5
|
public static void WaitAny(IEnumerable<Operator> operations){while (operations.All(o => !o.Middle.IsCompleted)) Thread.Sleep(100);} |
等待时间可以自定义
ActionInvoke
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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
|
1
2
3
4
5
6
7
8
9
10
|
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
|
1
2
3
4
5
6
7
8
9
10
11
|
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遍历
|
1
2
3
4
5
6
7
8
9
10
11
|
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
|
1
2
3
4
|
public static IEnumerable<Operator> Foreach<TParameter>(IEnumerable<TParameter> items, Action<TParameter> action){ return items.Select(t => Invoke(action, t)).ToList();} |
ForeachFunc
|
1
2
3
4
|
public static IEnumerable<Operator> Foreach<TParameter, TResult>(IEnumerable<TParameter> items, Func<TParameter, TResult> func){ return items.Select(parameter => Invoke(func, parameter)).ToList();} |
如何使用
无返回值异步方法调用
|
1
2
3
4
|
public void DoSomeThing(){//TODO:} |
通过Asynchronous.Invoke(DoSomeThing) 执行
|
1
2
3
4
|
public void DoSomeThing(string parameter){//TODO:} |
通过Asynchronous.Invoke(DoSomeThing,parameter) 执行
有返回值异步方法调用
|
1
2
3
4
|
public string DoSomeThing(){//TODO:} |
通过Asynchronous.Invoke(()=>DoSomeThing())执行
|
1
2
3
4
|
public string DoSomeThing(string parameter){//TODO:} |
通过Asynchronous.Invoke(()=>DoSomeThing(parameter))执行,或者也可以传入参数通过Asynchronous.Invoke(p=>DoSomeThing(p),parameter)
无返回值Foreach
|
1
2
3
4
5
|
public void Test{int[] parameters = {1,2,3,4,5};Asynchronous.Foreach(parameters,Console.WriteLine);} |
有返回值Foreach
|
1
2
3
4
5
6
7
8
|
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倍,然后输出
异步执行完再执行
|
1
2
3
4
5
6
|
public void Test{int[] parameters = {1,2,3,4,5};var operators = Asynchronous.Foreach(parameters,p=> p*2);Asynchrous.ContinueWithAsync(operators,Console.WriteLine,"执行完成");} |
每次执行完继续执行
可能有时候我们需要遍历一个集合,每个元素处理完成后我们需要输出XX处理完成
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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) { //之前执行时产生未处理的异常,这里可以捕获到 } });});} |
可以实现链式异步操作
|
1
2
3
4
5
6
|
public void Chain(){Asynchronous.Invoke(Console.WriteLine,1).ContinueWithAsync(Console.WriteLine,2).ContinueWithAsync(Console.WriteLine,3)} |
这样会按步骤输出1,2,3
结束语
以上只是列出了部分重载方法,其他重载方法无非就是加参数,本质实际是一样的。
希望对大家的学习有所帮助,在这祝大家新年快乐,新的一年大家一起努力。

发表评论