asynchronous call(异步调用)
一个可以无需等待被调用函式的返回值就让操作继续进行的方法
基本介绍
- 中文名:异步调用
- 外文名:asynchronous call
- 领域:函式
- 杰作:执行绪
- 。:。
举例
异步调用就是你 喊 你朋友吃饭 ,你朋友说知道了 ,待会忙完去找你 ,你就去做别的了。
同步调用就是你 喊 你朋友吃饭 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你们一起去。
实战用法
作业系统发展到今天已经十分精巧,执行绪就是其中一个杰作。作业系统把 CPU 处理时间划分成许多短暂时间片,在时间 T1 执行一个执行绪的指令,到时间 T2又执行下一执行绪的指令,各执行绪轮流执行,结果好象是所有执行绪在并肩前进。这样,编程时可以创建多个执行绪,在同一期间执行,各执行绪可以“并行”完成不同的任务。
在单执行绪方式下,计算机是一台严格意义上的冯·诺依曼式机器,一段代码调用另一段代码时,只能採用同步调用,必须等待这段代码执行完返回结果后,调用方才能继续往下执行。有了多执行绪的支持,可以採用异步调用,调用方和被调方可以属于两个不同的执行绪,调用方启动被调方执行绪后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,通过某种手段通知调用方:结果已经出来,请酌情处理。
计算机中有些处理比较耗时。调用这种处理代码时,调用方如果站在那里苦苦等待,会严重影响程式性能。例如,某个程式启动后如果需要打开档案读出其中的数据,再根据这些数据进行一系列初始化处理,程式主视窗将迟迟不能显示,让用户感到这个程式怎幺等半天也不出来,太差劲了。藉助异步调用可以把问题轻鬆化解:把整个初始化处理放进一个单独执行绪,主执行绪启动此执行绪后接着往下走,让主视窗瞬间显示出来。等用户盯着视窗犯呆时,初始化处理就在背后悄悄完成了。程式开始稳定运行以后,还可以继续使用这种技巧改善人机互动的瞬时反应。用户点击滑鼠时,所激发的操作如果较费时,再点击滑鼠将不会立即反应,整个程式显得很沉重。藉助异步调用处理费时的操作,让主执行绪随时恭候下一条讯息,用户点击滑鼠时感到轻鬆快捷,肯定会对软体产生好感。
异步调用外部数据处理
异步调用用来处理从外部输入的数据特别有效。假如计算机需要从一台低速设备索取数据,然后是一段冗长的数据处理过程,採用同步调用显然很不合算:计算机先向外部设备发出请求,然后等待数据输入;而外部设备向计算机传送数据后,也要等待计算机完成数据处理后再发出下一条数据请求。双方都有一段等待期,拉长了整个处理过程。其实,计算机可以在处理数据之前先发出下一条数据请求,然后立即去处理数据。如果数据处理比数据採集快,要等待的只有计算机,外部设备可以连续不停地採集数据。如果计算机同时连线多台输入设备,可以轮流向各台设备发出数据请求,并随时处理每台设备发来的数据,整个系统可以保持连续高速运转。编程的关键是把数据索取代码和数据处理代码分别归属两个不同的执行绪。数据处理代码调用一个数据请求异步函式,然后逕自处理手头的数据。待下一组数据到来后,数据处理执行绪将收到通知,结束 wait 状态,发出下一条数据请求,然后继续处理数据。
异步调用时,调用方不等被调方返回结果就转身离去,因此必须有一种机制让被调方有了结果时能通知调用方。在同一进程中有很多手段可以利用,笔者常用的手段是回调、互斥对象和讯息。
回调方式很简单:调用异步函式时在参数中放入一个函式地址,异步函式保存此地址,待有了结果后回调此函式便可以向调用方发出通知。如果把异步函式包装进一个对象中,可以用事件取代回调函式地址,通过事件处理例程向调用方发通知。
mutex是 Windows系统提供的一个常用同步对象,以在异步处理中对齐不同执行绪之间的步点。如果调用方暂时无事可做,可以调用 wait 函式等在那里,此时 mutex处于 nonsignaled 状态。当被调方出来结果之后,把 mutex对象置于 signaled 状态,wait函式便自动结束等待,使调用方重新动作起来,从被调方取出处理结果。这种方式比回调方式要複杂一些,速度也相对较慢,但有很大的灵活性,可以搞出很多花样以适应比较複杂的处理系统。
藉助 Windows讯息发通知是个不错的选择,既简单又安全。程式中定义一个用户讯息,并由调用方準备好讯息处理例程。被调方出来结果之后立即向调用方传送此讯息,并通过WParam 和 LParam 这两个参数传送结果。讯息总是与视窗 handle关联,因此调用方必须藉助一个视窗才能接收讯息,这是其不方便之处。另外,通过讯息联络会影响速度,需要高速处理时回调方式更有优势。
如果调用方和被调方分属两个不同的进程,由于记忆体空间的隔阂,一般是採用 Windows讯息发通知比较简单可靠,被调方可以藉助讯息本身向调用方传送数据。event对象也可以通过名称在不同进程间共享,但只能发通知,本身无法传送数据,需要藉助 Windows 讯息和 FileMapping等记忆体共享手段或藉助 MailSlot 和 Pipe 等通信手段。
异步调用原理
异步调用原理并不複杂,但实际使用时容易出莫名其妙的问题,特别是不同执行绪共享代码或共享数据时容易出问题,编程时需要时时注意是否存在这样的共享,并通过各种状态标誌避免冲突。Windows 系统提供的 mutex 对象用在这里特别方便。mutex同一时刻只能有一个管辖者。一个执行绪放弃管辖权后,另一执行绪才能接管。当某执行绪执行到敏感区之前先接管 mutex,使其他执行绪被 wait函式堵在身后;脱离敏感区之后立即放弃管辖权,使 wait函式结束等待,另一个执行绪便有机会光临此敏感区。这样就可以有效避免多个执行绪进入同一敏感区。
由于异步调用容易出问题,要设计一个安全高效的编程方案需要比较多的设计经验,所以最好不要滥用异步调用。同步调用毕竟让人更舒服些:不管程式走到哪里,只要死盯着移动点就能心中有数,不至于象异步调用那样,总有一种四面受敌、惶惶不安的感觉。必要时甚至可以把异步函式转换为同步函式。方法很简单:调用异步函式后马上调用 wait 函式等在那里,待异步函式返回结果后再继续往下走。
异步调用使用方法
测试方法和异步委託
四个示例全部使用同一个长期运行的测试方法 TestMethod。该方法显示一个表明它已开始处理的控制台信息,休眠几秒钟,然后结束。TestMethod 有一个 out 参数(在 Visual Basic 中为 ByRef),它演示了如何将这些参数添加到 BeginInvoke 和 EndInvoke 的签名中。您可以用类似的方式处理 ref 参数(在 Visual Basic 中为 ByRef)。
下面的代码示例显示 TestMethod 以及代表 TestMethod 的委託;若要使用任一示例,请将示例代码追加到这段代码中。
注意 为了简化这些示例,TestMethod 在独立于 Main() 的类中声明。或者,TestMethod 可以是包含 Main() 的同一类中的 static 方法(在 Visual Basic 中为 Shared)。
使用 EndInvoke 等待异步调用
异步执行方法的最简单方式是以 BeginInvoke 开始,对主执行绪执行一些操作,然后调用 EndInvoke。EndInvoke 直到C#异步调用完成后才返回。这种技术非常适合档案或网路操作,但是由于它阻塞 EndInvoke,所以不要从用户界面的服务执行绪中使用它。
使用 WaitHandle 等待异步调用
等待 WaitHandle 是一项常用的执行绪同步技术。您可以使用由 BeginInvoke 返回的 IAsyncResult 的 AsyncWaitHandle 属性来获取 WaitHandle。C#异步调用完成时会发出 WaitHandle 信号,而您可以通过调用它的 WaitOne 等待它。
如果您使用 WaitHandle,则在C#异步调用完成之后,但在通过调用 EndInvoke 检索结果之前,可以执行其他处理。
轮询异步调用完成
您可以使用由 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 属性来发现C#异步调用何时完成。从用户界面的服务执行绪中进行C#异步调用时可以执行此操作。轮询完成允许用户界面执行绪继续处理用户输入。