《CLR via C#》读书笔记-异步编程(四)

APM是一种编程方式,而不是C#编程语言的一种新特性(虽然实现APM而提供BeginXXX、EndXXX方法,但这个并没有改变XXX方法的本质,因此APM只是一种编程方式)
APM异常
正式因为APM只是一种编程方式,因此其异常处理方式与正常的方式一致。但有一点需要说明,一般情况下,会将try语句块放置于调用EndXXX的方法内部。
APM与计算限制
在《CLR via C#》中多线程和异步以及后面同步锁的这些部分中都包含了“计算限制”这个名称,说一下个人理解。计算限制操作是指利用计算机的性能去完成非I/O类型操作的操作统称。
APM编程模式一般是用于I/O相关的操作中,但是其仍然可以适用于“计算限制操作”中。其原理如下:定义好委托之后,编译器会将委托编译,并生成相关的方法,其中就有一个BeginInvoke和EndInvoke方法,而有了这两个方法就可以在编程时采用APM编程方式。以.NET自带的Func

public delegate TResult Func<in T, out TResult>(T arg)

定义完委托后,编译器会将委托编译为如下:

public sealed class Func<T,TResult>:MulticastDelegate{
    public Func(Object object,IntPtr method);
    public TResult Invoke(T arg);
    public IAsyncResult BeginInvoke(T arg,AsyncCallBack callback,Object object);
    public TResult EndInvoke(IAsyncResult result);
}

就是通过第三个、第四个方法完成了计算限制操作的APM编程。其中callback回调方法的委托的具体定义为:

[SerializableAttribute]
[ComVisibleAttribute(true)]
public delegate void AsyncCallback(
    IAsyncResult ar
)

计算限制操作使用APM编程的例子如下:
假设需完成某求和的方法,方法如下:

private static UInt64 Sum(UInt64 n){
    UInt64 sum=0;
    for(UInt64 i=0;i<n;i++){
        checked{
            sum+=i;
        }
    }
    return sum;
}

这个方法正好符合Func

public static void Main(){
    Func<UInt64,UInt64> sumDelegate=Sum;

    sumDelegate.BeginInvoke(1000000,SumIsDone,sumDelegate); //最后一个参数很有意思

    Console.ReadLine();
}

private static void SumIsDone(IAsyncResult result){
    var sumDelegate = (Func<UInt64,UInt64>)result.AsyncState;
    try{
        UInt64 theSumResult=sumDelegate.EndInvoke(result);
        Console.WriteLine("最终求和结果为:"+theSumResult);
    }catch(Exception ex){
        throw ex;
    }
}

APM注意事项
1、默认情况下,CLR在异步操作完成后通过线程池调用预先定义的回调方法。若不使用系统默认,而是通过手动查询异步操作是否完成,进而再去调用回调方法,这也是可行的。我在《CLR via C#》读书笔记-异步编程(三) 中说明了如何使用IAsyncResult对象属性来判断异步是否完成。但是不建议这样做。这样会导致线程阻塞,线程池创建新的线程。
2、APM编程中一定要调用EndXXX方法
3、在调用EndXXX方法时,一定要使用与调用BeginXXX相同的对象。在上面的例子的有一行注释“最后一个参数很有意思”,这个参数的含义就是将其封装在IAsyncResult的AsyncState属性中,这样就可以在调用EndXXX的方法中得到,在调用EndXXX时能够保证,其与调用BeginXXX的对象是相同的。
4、APM编程方式无取消机制(从Vista开始,Win32中新增了一个CancelSynchronousIO函数,可以允许一个线程取消另外一个线程执行同步的操作)
5、APM会导致内存消耗。因为在异步操作创建时就会创建IAsyncResult对象。不过这种对内存的占用还是小
6、有些操作必须是同步的,因此在这些过程中是不能使用APM编程方式的。例如通过FileStream创建文件只能是一个同步操作。特别明显的感受就是在网络映射磁盘或者是U盘中创建文件,在创建文件的时候就会出现卡死的的现象。
7、FileStream特有的问题。FileStream很有意思,其在初始化的时候需要通过File.ASynchronous标志位进行标示该对象上所有的操作时同步还是异步。若使用默认时(即,采用同步的方式),虽然也可以调用BeginRead,但是其内部是通过创建一个新的线程模拟异步操作的。而这个动作是“纯属浪费,而且会影响到性能”。因此,在使用FileStream时,要一开始就要想明白是采用异步还是同步。若采用异步方式,就要调用BeginXXX,若采用同步,就采用XXX方法。这样才能保证可以获得最佳的性能。若在使用的过程中,既需要同步使用,又需要异步使用,那就设定FileStream为异步吧!或者再笨的方法就是创建两个FileStream,一个同步操作,一个异步操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值