Use APM to perform computing-restricted asynchronous operations
We can call any method through APM, but first, we need to define a method with the same signature as the method we want to call. Take the method of calculating the sum of 1 to n as an example (this is a computation intensive task that does not perform any I/O operations ).
Private Static uint64 sum (uint64 N)
{
Uint64 sum = 0;
For (uint64 I = 1; I <= N; I ++)
{
Checked
{
// The checked code is used here to throw an exception when the result sum does not match the type uint64
Sum + = I;
}
}
Return sum;
}
If n is very large, the sum method takes a long time. Therefore, we want to execute this method asynchronously. To do this, first define a delegate with the same signature as the method (SUM) that you want to call asynchronously.
internal delegate UInt64 SumDelegate(UInt64 n);
C # The Compiler compiles the previous line of code into a class definition. Its Logic format is as follows:
internal sealed class SumDelegate : MulticastDelegate
{
public SumDelegate(Object object, IntPtr method);
public UInt64 Invoke(UInt64 n);
public IAsyncResult BeginInvoke(UInt64 n, AsyncCallback callback, Object object);
public UInt64 EndInvoke(IAsyncResult result);
}
When a delegate is defined in C # source code, the compiler usually generates a class containing the begininvoke and endinvoke methods. The begininvoke method has the same parameters as the delegate definition, and two additional parameters asynccallback and object are added. All begininvoke Methods return an iasyncresult object. The endinvoke method has only one parameter, iasyncresult. The endinvoke method returns the data type returned by the delegate signature. The following code demonstrates how to use the method callback aggregation technique to asynchronously call the sum method:
Public static void main ()
{
// Initialize a delegate variable to reference the method we want to call Asynchronously
Sumdelegate = sum;
// Call the method of using the thread pool thread
Sumdelegate. begininvoke( 100000000, sumisdone, sumdelegate );
// Execute some other code here
// Suspends the main thread for demonstration
Console. Readline ();
}
Private Static void sumisdone (iasyncresult AR)
{
// Extract the sumdelegate object from the iasyncresult object
Sumdelegate = (sumdelegate) Ar. asyncstate;
// Obtain the result
Uint64 sum = sumdelegate. endinvoke (AR );
// Display the result
Console. writeline ("sum = {0}", sum );
}
The begininvoke method adds operations to the queue of the thread pool. The threads in the thread pool will be awakened, work items will be taken out of the queue, and then the operation restricted by calculation will be called (sum in this example ). Generally, when a thread in the thread pool returns from the execution method, the thread returns to the thread pool. However, the begininvoke method uses the name of a method (sumisdone) as its penultimate parameter. Therefore, when the sum method returns, the thread in the thread pool will not return to the thread pool. Instead, it will call the sumisdone method. That is to say, the callback method is called when the operation restricted by computing is completed.
APM and exceptions
Whenever you call the beginxxx method, it may throw an exception. Windows will tell the application that the asynchronous operation has been completed incorrectly. To achieve this, Windows will send a notification to the CLR thread pool. Finally, our code aggregates the operation results by calling the endxxx method. Generally, the endxxx method returns the operation result to the code, but if the operation fails, the endxxx method throws an exception. Therefore, we should consider handling exceptions when calling the endxxx method. As follows:
Private Static void sumisdone (iasyncresult AR)
{
// Extract the sumdelegate object from the iasyncresult object
Sumdelegate = (sumdelegate) Ar. asyncstate;
Try
{
Uint64 sum = sumdelegate. endinvoke (AR );
Console. writeline ("sum = {0}", sum );
}
Catch (overflowexception)
{
Console. writeline ("sum can't be shown: number is too big .");
}
}