Rumah > pembangunan bahagian belakang > Tutorial C#.Net > C#异步之APM模式异步程序开发的示例分享

C#异步之APM模式异步程序开发的示例分享

黄舟
Lepaskan: 2018-05-11 11:32:56
asal
2002 orang telah melayarinya

C#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,C#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下C#中的异步编程的发展历程。广告一下:喜欢我文章的朋友,请点下面的“关注我”。谢谢

我是2004年接触并使用C#的,那时C#版本为1.1,所以我们就从就那个时候谈起。那时在大学里自己看书写程序,所写的程序大都是同步程序,最多启动个线程........其实在C#1.1的时代已有完整的异步编程解决方案,那就是APM(异步编程模型)。如果还有不了解“同步程序、异步程序”的请自行百度哦。

APM异步编程模型最具代表性的特点是:一个异步功能由以Begin开头、End开头的两个方法组成。Begin开头的方法表示启动异步功能的执行,End开头的方法表示等待异步功能执行结束并返回执行结果。下面是一个模拟的实现方式(后面将编写标准的APM模型异步实现):

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

public class Worker

    {       

    public int A { get; set; }       

    public int B { get; set; }       

    private int R { get; set; }

        ManualResetEvent et;       

        public void BeginWork(Action action)

        {

            et = new ManualResetEvent(false);           

            new Thread(() =>

            {

                R = A + B;

                Thread.Sleep(1000);

                et.Set();               

                if(null != action)

                {

                    action();

                }

            }).Start();

        }        public int EndWork()

        {            if(null == et)

            {                t

            hrow new Exception("调用EndWork前,需要先调用BeginWork");

            }           

            else

            {

                et.WaitOne();               

                return R;

            }

 

        }

    }

Salin selepas log masuk

1

2

3

4

5

6

7

8

9

10

static void Main(string[] args)

{

   Worker w = new Worker();

    w.BeginWork(()=> {

        Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId,

            w.EndWork());

    });

    Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);

    Console.ReadLine();

}

Salin selepas log masuk

在上面的模拟APM模型中我们使用了 Thread、ManualResetEvent,如果你对多线程和ManualResetEvent不熟悉C#中使用异步编程不可避免的会涉及到多线程的知识,虽然微软在Framework中做了很多封装,但朋友们应该掌握其本质。

上面模拟的APM异步模型之所以简单,是因为C#发展过程中引入了很多优秀的语法规则。上例我们较多的使用了Lambda表达式,如果你不熟悉 匿名委托与lambda表达式可看我之前的Bolg《匿名委托与Lambda表达式》。上面做了如此多的广告,下面我们来看一下标准的APM模型如何实现异步编程。

IAsyncResult接口

IAsyncResult接口定义了异步功能的状态,该接口具体属性及含义如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//     表示异步操作的状态。

 [ComVisible(true)]    public interface IAsyncResult

 {        //

     // 摘要:        //     获取一个值,该值指示异步操作是否已完成。        //

     // 返回结果:        //     如果操作已完成,则为 true;否则为 false。

     bool IsCompleted { get; }        //

     // 摘要:        //     获取用于等待异步操作完成的 System.Threading.WaitHandle。        //

     // 返回结果:        //     用于等待异步操作完成的 System.Threading.WaitHandle。

     WaitHandle AsyncWaitHandle { get; }        //

     // 摘要:        //     获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。        //

     // 返回结果:        //     一个用户定义的对象,限定或包含有关异步操作的信息。

     object AsyncState { get; }        //

     // 摘要:        //     获取一个值,该值指示异步操作是否同步完成。        //

     // 返回结果:        //     如果异步操作同步完成,则为 true;否则为 false。

     bool CompletedSynchronously { get; }

 }

Salin selepas log masuk

注意:模型示例1中的 ManualResetEvent 继承自 WaitHandle<br/>APM传说实现方式<br/>在了解了IAsyncResult接口后,我们来通过实现 IAsyncResult 接口的方式完成对模拟示例的改写工作,代码如下:

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

public class NewWorker

{        public class WorkerAsyncResult : IAsyncResult

    {

        AsyncCallback callback;           

        public WorkerAsyncResult(int a,int b, AsyncCallback callback, object asyncState) {

            A = a;

            B = b;

            state = asyncState;               

            this.callback = callback;               

            new Thread(Count).Start(this);

        }           

        public int A { get; set; }           

        public int B { get; set; }           

        public int R { get; private set; }           

        private object state;           

        public object AsyncState

        {               

        get

            {                   

            return state;

            }

        }           

        private ManualResetEvent waitHandle;           

        public WaitHandle AsyncWaitHandle

        {               

        get

            {                   

            if (null == waitHandle)

                {

                    waitHandle = new ManualResetEvent(false);

                }                   

                return waitHandle;

            }

        }            private bool completedSynchronously;           

        public bool CompletedSynchronously

        {                get

            {                   

            return completedSynchronously;

            }

        }           

        private bool isCompleted;           

        public bool IsCompleted

        {               

        get

            {                   

            return isCompleted;

            }

        }           

        private static void Count(object state)

        {               

        var result = state as WorkerAsyncResult;

            result.R = result.A + result.B;

            Thread.Sleep(1000);

            result.completedSynchronously = false;

            result.isCompleted = true;

            ((ManualResetEvent)result.AsyncWaitHandle).Set();               

            if (result.callback != null)

            {

                result.callback(result);

            }

        }

    }       

    public int Num1 { get; set; }       

    public int Num2 { get; set; }       

    public IAsyncResult BeginWork(AsyncCallback userCallback, object asyncState)

    {

        IAsyncResult result = new WorkerAsyncResult(Num1,Num2,userCallback, asyncState);           

        return result;

    }        public int EndWork(IAsyncResult result)

    {

        WorkerAsyncResult r = result as WorkerAsyncResult;

        r.AsyncWaitHandle.WaitOne();            return r.R;

    }

}

Salin selepas log masuk

示例代码分析:

上面代码中NewWorker的内部类 WorkerAsyncResult 是关键点,它实现了 IAsyncResult 接口并由它来负责开启新线程完成计算工作。

在WorkerAsyncResult中增加了 A、B两个私有属性来存储用于计算的数值,一个对外可读不可写的属性R,用于存储WorkerAsyncResult内部运算的结果。AsyncWaitHandle属性由 ManualResetEvent 来充当,并在首次访问时创建ManualResetEvent(但不释放)。其他接口属性正常实现,没有什么可说。

WorkerAsyncResult 中新增 static Count 方法,参数 state 为调用Count方法的当前WorkerAsyncResult对象。Count 方法在 WorkerAsyncResult 对象的新启线程中运行,因此Thread.Sleep(1000)将阻塞新线程1秒中。然后设置当前WorkerAsyncResult对象是否同步完成为false,异步完成状态为true,释放ManualResetEvent通知以便等待线程获取通知进入执行状态,判断是否有异步执行结束回调委托,存在则回调之。

NewWorker 非常简单,Num1、Num2两个属性为要计算的数值。BeginWork 创建WorkerAsyncResult对象、并将要计算的两个数值Num1、Num2、userCallback回调委托、object 类型的 asyncState 传入要创建的WorkerAsyncResult对象。经过此步操作,WorkerAsyncResult对象获取了运算所需的所有数据、运算完成后的回调,并马上启动新线程进行运算(执行WorkerAsyncResult.Count方法)。

因为WorkerAsyncResult.Count执行在新线程中,在该线程外部无法准确获知新线程的状态。为了满足外部线程与新线程同步的需求,在NewWorker中增加EndWork方法,参数类型为IAsyncResult。要调用EndWork方法应传入BeginWork 获取的WorkerAsyncResult对象,EndWork方法获取WorkerAsyncResult对象后,调用WorkerAsyncResult.AsyncWaitHandle.WaitOne()方法,等待获取ManualResetEvent通知,在获取到通知时运算线程已运算结束(线程并未结束),下一步获取运算结果R并返回。

接下来是NewWorker调用程序,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

static void Main(string[] args)

{

    NewWorker w2 = new NewWorker();

    w2.Num1 = 10;

    w2.Num2 = 12;

    IAsyncResult r = null;

    r = w2.BeginWork((obj) => {

    Console.WriteLine("Thread Id:{0},Count:{1}",Thread.CurrentThread.ManagedThreadId,

    w2.EndWork(r));

    }, null);

    Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);

    Console.ReadLine();

}

Salin selepas log masuk

下图我简单画的程序调用过程,有助于各位朋友理解:

标准的APM模型异步编程,对应开发人员来说过于复杂。因此通过实现 IAsyncResult 接口进行异步编程,就是传说中的中看不中用(罪过罪过.....)。

Delegate异步编程(APM 标准实现)

C#中委托天生支持异步调用(APM模型),任何委托对象后"."就会发现BeginInvoke、EndInvoke、Invoke三个方法。BeginInvoke为异步方式调用委托、EndInvoke等待委托的异步调用结束、Invoke同步方式调用委托。因此上面的标准APM实例,可借助 delegate 进行如下简化。

上面NewWorker使用委托方式改写如下:

1

<br/>

Salin selepas log masuk

<br/>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public class NewWorker2

{

    Func<int, int, int> action;        public NewWorker2()

    {

        action = new Func<int, int, int>(Work);

    }        public IAsyncResult BeginWork(AsyncCallback callback, object state)

    {            dynamic obj = state;            return action.BeginInvoke(obj.A, obj.B, callback, this);

    }        public int EndWork(IAsyncResult asyncResult)

    {            try

        {                return action.EndInvoke(asyncResult);

        }            catch (Exception ex)

        {                throw ex;

        }

    }        private int Work(int a, int b)

    {

        Thread.Sleep(1000);            return a + b;

    }

}

Salin selepas log masuk

调用程序:

1

2

3

4

5

6

7

8

9

10

11

12

13

static void Main(string[] args)

{

    NewWorker2 w2 = new NewWorker2();

    IAsyncResult r = null;

    r = w2.BeginWork((obj) =>

    {

        Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId,

            w2.EndWork(r));

    }, new { A = 10, B = 11 });

    Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);

 

    Console.ReadLine();

}

Salin selepas log masuk

上面的使用委托进行APM异步编程,比实现 IAsyncResult 接口的方式精简太多、更易理解使用。因此这里建议朋友们 delegate 异步调用模型应该掌握起来,而通过实现 IAsyncResult 接口的传说方式看你的喜好吧。 

Atas ialah kandungan terperinci C#异步之APM模式异步程序开发的示例分享. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan