异步调用时,调用方不等被调方返回结果就转身离去,因此必须有一种机制让被调方有了结果时能通知调用方。在同一进程中有很多手段可以利用,笔者常用的手段是回调、event 对象和消息。
回调:回调方式很简单:调用异步函数时在参数中放入一个函数地址,异步函数保存此地址,待有了结果后回调此函数便可以向调用方发出通知。如果把异步函数包装进一个对象中,可以用事件取代回调函数地址,通过事件处理例程向调用方发通知。
event : event 是 Windows 系统提供的一个常用同步对象,以在异步处理中对齐不同线程之间的步点。如果调用方暂时无事可做,可以调用 wait 函数等在那里,此时 event 处于 nonsignaled 状态。当被调方出来结果之后,把 event 对象置于 signaled 状态,wait 函数便自动结束等待,使调用方重新动作起来,从被调方取出处理结果。这种方式比回调方式要复杂一些,速度也相对较慢,但有很大的灵活性,可以搞出很多花样以适应比较复杂的处理系统。
消息:借助 Windows 消息发通知是个不错的选择,既简单又安全。程序中定义一个用户消息,并由调用方准备好消息处理例程。被调方出来结果之后立即向调用方发送此消息,并通过 WParam 和 LParam 这两个参数传送结果。消息总是与窗口 handle 关联,因此调用方必须借助一个窗口才能接收消息,这是其不方便之处。另外,通过消息联络会影响速度,需要高速处理时回调方式更有优势。
如果调用方和被调方分属两个不同的进程,由于内存空间的隔阂,一般是采用 Windows 消息发通知比较简单可靠,被调方可以借助消息本身向调用方传送数据。event 对象也可以通过名称在不同进程间共享,但只能发通知,本身无法传送数据,需要借助 Windows 消息和 FileMapping 等内存共享手段或借助 MailSlot 和 Pipe 等通信手段。
异步调用原理并不复杂,但实际使用时容易出莫名其妙的问题,特别是不同线程共享代码或共享数据时容易出问题,编程时需要时时注意是否存在这样的共享,并通过各种状态标志避免冲突。Windows 系统提供的 mutex 对象用在这里特别方便。mutex 同一时刻只能有一个管辖者。一个线程放弃管辖权后,另一线程才能接管。当某线程执行到敏感区之前先接管 mutex,使其他线程被 wait 函数堵在身后;脱离敏感区之后立即放弃管辖权,使 wait 函数结束等待,另一个线程便有机会光临此敏感区。这样就可以有效避免多个线程进入同一敏感区。
由于异步调用容易出问题,要设计一个安全高效的编程方案需要比较多的设计经验,所以最好不要滥用异步调用。同步调用毕竟让人更舒服些:不管程序走到哪里,只要死盯着移动点就能心中有数,不至于象异步调用那样,总有一种四面受敌、惶惶不安的感觉。必要时甚至可以把异步函数转换为同步函数。方法很简单:调用异步函数后马上调用 wait 函数等在那里,待异步函数返回结果后再继续往下走。
3.如何写一个异步函数
我们平常编程写的函数 几乎都是同步调用函数,那么我们如何写一个异步执行的函数呢?!我想这个问题也许是哪些比较喜欢专研的程序员或者具有专研精神的人士回提出的问题吧!我们很多人已经习惯了windows系统提供的一些异步机制,使用这些异步机制我们很快的就能实现一些异步操作甚至可以很容易的实现一个异步执行的函数;但是我们研究过实现一个“异步函数”的本质吗?!
在单线程的系统中,所以的指令执行都是顺序执行的,这就暗示了如果一个函数A中调用了函数B,则A必须等到B执行后才能继续执行A中剩下的代码。
在多线程中,如果我们有一个threadA线程,在该线程中调用了一个函数C,而该C函数我们想将它实现成异步执行的,而异步执行必须要有多线程支持;如果我们在Windows中编写程序,创建一个线程是很简单只要使用
HANDLE WINAPI CreateThread( __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags, __out_opt LPDWORD lpThreadId ); 函数就可以创建一个线程。
那么我们按如下方式可以实现一个异步的FuncC函数: (1)先把你要异步完成的工作单独写成要给函数,如 DWORD WINAPI AsyncronousThread( LPVOID lpParameter // thread data
) { ..... } (2)在函数FuncC中使用CreateThtread函数将(1)中的函数创建一成一个线程,然后直接返回。 void FuncC(void) { ..... CreateThread(....,AsyncronousThread,...); return; } 当然,写一个异步函数的方法很多,但是一个本质不会变,就是必须要依据多线程才能实现。
|