.NET组件程序设计——异步调用

引言

在进行一个对象的方法调用时,对象执行调用期间客户端通常是被阻塞的,且只有该方法执行完毕并且返回时,控制权才会回到客户端。当提及这样的需求,作为开发人员的你会使用何种方式完成呢?通常情况下我们都会使用多线程来满足这种需求。然而对于个人而言,实现一个强健的异步调用执行是不堪重负的,需要呕心沥血的设计、实现、测试。不过,在使用.Net开发时,我们可以使用异步调用方式来满足客户需求。

 

什么是异步调用

.Net设计师考虑到了这个问题,为了让你集中精力在手头的业务问题的解决,而非繁杂的异步调用问题,提出了异步调用。有的时候你需要把调用的方法A放在后台执行,此时控制权可以立即返回客户端,执行完方法A后以某种方式通知客户端A方法已经执行完毕,这种执行模式被称为异步方法调。实现这种操作就需要使用异步调用。在学习异步调用之前,首先我们要了解委托的内部原理。

 

使用异步调用离不开委托

我们新建一个Windows Form应用程序。

项目中添加一个计算类Calculator,包含加、减法运算 

public class Calculator
{
    public int Add(int arg1, int arg2)
    {
        return arg1 + arg2;
    }
    public int Subtract(int arg1, int arg2)
    {
        return arg1 - arg2;
    }
}

 在Form1中添加一个按钮

 

在Form1类中添加一个委托

public delegate int BinaryOperation(int arg1, int arg2);

 双击按钮添加Click事件

private void button1_Click(object sender, EventArgs e)
{
    Calculator c = new Calculator();
    // 也可以使用+=,后面有解释,与这里知识无关
     BinaryOperation oppDel = c.Add;
    int result = 0;
    result = oppDel(2, 3);
    MessageBox.Show("result = " + result);
}

 运行后显示结果:result=5


委托的原理

运行到此,聪明的人会在思考一个问题,这种委托方式内部是如何实现的。

编译器发现BinaryOperation委托,他会将BinaryOperation委托代码编译为

public sealed class BinaryOperation : MulticastDelegate
{
	public BinaryOperation(object target, int methodPtr){}

	public virtual int Invoke(int arg1, int arg2){}

	public virtual IAsyncResult Begin(int arg1, int arg2, AsyncCallback callback, object asyncState){}

	public virtual int EndInvoke(IAsyncResult result){}
}


当我们调用委托oppDel(2, 3)时,编译器会转变对Invoke(int arg1, int arg2)的调用。Invoke方法会阻塞调用者,在调用者线程上执行这个方法,然后把控制权交给调用者。

BinaryOperation oppDel = c.Add;
int result = 0;
result = oppDel(2, 3);

BinaryOperation类是从MulticastDelegate类派生而来的,MulticastDelegate提供了每个委托都有的内部委托列表实现,而这些委托均派生自抽象类Delegate(本博客事件章节有详细介绍)。编译器也声明了两个方法来管理异步方法调用。BeginInvoke()和EndInvoke(),而他们的正确使用是本章节的主题。

 

异步调用的编程模型
要支持异步调用,多线程是必不可少的。如果.Net为每个异步调用方法都创建一个新线程,无疑是一个巨大的资源浪费,同时也会导致性能的损失。一个比较好的方法是使用已经创建的工作线程池。.Net中正有这样的线程池,被称为.Net线程池。在.Net中隐蔽了异步调用的交互。这里我们主要阐述阻塞、等待、轮循、完成回调。

总的思路是:BeginInvoke()发起一个异步方法调用,在将请求放入队列时,会短暂的阻塞客户端,然后控制权又回到客户端。EndInvoke()管理方法的完成,包括输出参数和返回值,以及错误处理。

 

使用BeginInvoke()和EndInvoke()

 编译器生成的BeginInvoke()和EndInvoke()有如下格式:

public virtual IAsyncResult BeginInvoke(<input and input/output parameters>, AsyncCallback callback, object asyncState);

public virtual <return value> EndInvoke(<outpub and input/output parameters>, IAsyncResult asyncResult);

BeginInvoke接受两个额外参数:AsyncCallback回调和asyncState对象,这两个不在原始委托签名中。回调参数实际上是一个委托对象,代表对一个回调方法的引用,该方法用于接收方法完成事件的通知。asyncState是一个通用对象,该对象传递任何状态信息,这些信息是处理方法完成的一方所必须的。这两个参数是可选的,可以传递两个null或选择其一。

Calculator c = new Calculator();
BinaryOperation oppDel = c.Add;
oppDel.BeginInvoke(2, 3, null, null);

通过这样的方式,任意一个类都支持同步或异步调用。

 

如何获得异步方法调用结果

从上面的代码不难看出BeginInvoke方法返回一个实现了IAsyncResult接口对象,IAsyncResult定义如下:

public interface IAsyncResult
{
    object AsyncState{get;}
    WaitHandle AsyncWaitHandle{get;}
    bool CompletedSynchronously{get;}
    bool IsCompleted{get;}
}

我们可以看到,在.Net中使用IAsynResult对象唯一标示BeginInvoke方法的返回值,你可以把IAsyncResult对象传递给EndInvoke(),以此标识需要取得那个异步方法的执行结果。

界面添加一个《异步计算1》按钮,

Click事件

private void button2_Click(object sender, EventArgs e)
{
    Calculator c = new Calculator();
    BinaryOperation oppDel = c.Add;
    IAsyncResult asyncResult1 = oppDel.BeginInvoke(2, 3, null, null);
    IAsyncResult asyncResult2 = oppDel.BeginInvoke(5, 5, null, null);

    // 注意输出顺序
     MessageBox.Show(oppDel.EndInvoke(asyncResult2).ToString());
    MessageBox.Show(oppDel.EndInvoke(asyncResult1).ToString());
}

输出结果10,5
上面案例展示几个关键点:

1、EndInvoke主要用途是获取输出参数的和方法的返回值,EndInvoke阻塞了调用者直到他等待的方法返回值。

2、同一个委托对象会有多个异步调用,如oppDel委托对象,我们进行了两次BeginInvoke。我们通过传入EndInvoke的IAsyncResult对象标识对应关系。

3、异步调用顺序是不可测的,所以不要对次序做任何假设。


 

 

 

 

 

异步调用的优点

异步调用可以提高应用程序的可用性

提高其吞吐量和性能

提供更好的向外扩展能力

 

 

 

 

 

 

 

 

持续更新中...

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值