You want an asynchronous delegate to operate only within an allowed time span. If it is not finished processing within this time frame, the operation will time out. If the asynchronous delegate times out, it must perform any cleanup before the thread it is running on is terminated.
The WaitHandle.WaitOne
method can indicate when an asynchronous operation times out. The
code on the invoking thread needs to periodically wake up to do some
work along with timing-out after a specific period of time. Use the
approach shown in the following code, which will wake up every 20
milliseconds to do some processing. This method also times out after
a specific number of wait/process cycles (note that this code will
actually time out after more than two seconds of operation since work
is being done between the wait
cycles):
public class AsyncAction { public void TimeOutWakeAsyncDelegate( ) { AsyncInvoke method1 = new AsyncInvoke(TestAsyncInvoke.Method1); // Define the AsyncCallback delegate to catch EndInvoke if we timeout. AsyncCallback callBack = new AsyncCallback(TestAsyncInvoke.CallBack); // Set up the BeginInvoke method with the callback and delegate IAsyncResult asyncResult = method1.BeginInvoke(callBack,method1); int counter = 0; while (counter <= 25 && !asyncResult.AsyncWaitHandle.WaitOne(20, true)) { counter++; Console.WriteLine("Processing..."); } if (asyncResult.IsCompleted) { int retVal = method1.EndInvoke(asyncResult); Console.WriteLine("retVal (TimeOut): " + retVal); } else { Console.WriteLine("TimedOut"); } // Clean up asyncResult.AsyncWaitHandle.Close( ); } }
The following code defines the AsyncInvoke
delegate and the asynchronously invoked static method
TestAsyncInvoke.Method1
:
public delegate int AsyncInvoke( ); public class TestAsyncInvoke { public static int Method1( ) { Console.WriteLine("Invoked Method1"); return (1); } public static void CallBack(IAsyncResult ar) { // Retrieve the delegate. AsyncInvoke ai = (AsyncInvoke) ar.AsyncState; // Call EndInvoke to retrieve the results. int retVal = ai.EndInvoke(ar); Console.WriteLine("retVal: " + retVal); } }
The asynchronous delegates in this recipe are created and invoked in
the same fashion as the asynchronous delegate in Recipe 15.4. However, instead of using the
IsCompleted
property to determine whether the
asynchronous delegate is finished processing,
WaitHandle.WaitOne
is
used. This method blocks the thread that it is called on either
indefinitely or for a specified length of time. This method will stop
blocking the thread when it is signaled by the
ThreadPool
that the thread has completed or timed
out, and returns a true
indicating that the
asynchronous processing is finished and the calling thread has been
signaled. If the processing is not finished before the allotted
time-out value expires, WaitOne
returns
false
. Note that the WaitOne
method that accepts no parameters will block the calling thread
indefinitely.
It is usually a better idea to include a time-out value when using
the WaitOne
method, as this will prevent the
calling thread from being blocked forever if a deadlock situation
occurs (in which case the thread on which the
WaitOne
method waits is never signaled) or if the
thread running the asynchronous delegate never returns, such as when
entering into an infinite loop.
The
TimeOutAsyncDelegate
method in this recipe uses
the WaitOne
method to block the calling thread for
two seconds. If the asynchronous delegate has not finished processing
within this two-second period, the WaitOne
method
will return a false
. If the asynchronous delegate
finishes processing before the time-out value elapses, the calling
thread is signaled by the running thread that the asynchronous
delegate is finished and the WaitOne
method stops
blocking the calling thread and returns a value of
true
. If the WaitOne
method
returns true
, the EndInvoke
method should be called to retrieve any return values,
ref
parameter values, or out
parameter values.
The
TimeOutWakeAsyncDelegate
method in this recipe approaches the
time-out technique a little differently than the first method. The
TimeOutWakeAsyncDelegate
method will periodically
wake up (after 20 milliseconds) and perform some task on the calling
thread; unlike the TimeOutAsyncDelegate
method,
which will continue blocking for the allotted time frame and not wake
up. After 25 wait cycles, if the asynchronous delegate has not
finished processing, the while
loop will be
exited, essentially timing out the delegate. If the delegate finishes
processing before the 25 wait cycles have completed, the
while
loop is exited.
The
IsCompleted
property is checked next to determine
whether the asynchronous delegate has finished its processing at this
time. If it has finished, the EndInvoke
method is
called to obtain any return value, ref
parameter
values, or out
parameter values. Otherwise, the
delegate has not completed within the allotted time span and the
application should be informed that this thread has timed out.
The call to the Close
method of the
WaitHandle
object in effect performs the same
function as a Dispose
method. Any resources held
by this instance of the WaitHandle
object are
released.