“This is the 27th day of my participation in the August Gwen Challenge.
Why do asynchronous callbacks? As we all know, ordinary method operation is single thread, if there is a large operation in the middle of the process (such as: reading large files, mass operation database, network transmission, etc.), will lead to method blocking, performance in the interface is, program card or die, interface elements are not moving, do not respond. Asynchronous methods solve these problems very well. If you execute a method asynchronously, the application will immediately start a new thread to run your method, and the main thread including the interface will not die. Asynchronous calls do not reduce thread overhead; their main purpose is to keep the main thread from waiting on the function call synchronously, allowing it to continue executing the code below.
The BeginInvoke method can use threads to asynchronously execute the method pointed to by the delegate. The EndInvoke method returns the return value of the invoked method), or verifies that the method has been successfully invoked. When using BeginInvoke to invoke a method asynchronously, if the method is not finished executing, the EndInvoke method blocks until the invoked method is finished executing.
Generic template for asynchronous invocation
Asynchronous calls are made through delegates
IAsyncResult RTN = delegate variable The BeginInvoke (...) ; There are other things you can do in this section. The program is in asynchronous execution mode // blocking and waiting for the asynchronous call to end. EndInvoke(rtn);Copy the code
The heart of asynchronous invocation
Asynchronous calls are made through delegates, which define delegates:
private delegate long CalculateFolderSizeDelegate(string folderPath);
Copy the code
The decompilation result using Reflactor is as follows:
public sealed class CalculateFolderSizeDelegate: MulticastDelegate {public CalculateFolderSizeDelegate (Object target, intmethodPtr) {... } Public Virtual Long Invoke (string folderPath) {... } public Virtual IAsyncResult BeginInvoke(String folderPath, AsyncCallbackcallback Result, Object asyncState) {... } public Virtual Long EndInvoke(IAsyncResultresult) {... }}Copy the code
From this, we find that when we define a delegate, we actually define a delegate type, which has invoke, BeginInvoke(), EndInvoke() member methods, and these member methods can implement the one-step call mechanism. Let’s see how the format of these methods is defined. Okay
The BeginInvoke method is used to start the asynchronous invocation
The function declaration for BeginInvoke() :
Public IAsyncResult BeginInvoke(< input and output variables >, callback, additional information AsyncState)Copy the code
Function return value type:
Public interface IAsyncResult {// Summary: // Gets a user-defined object that qualifies or contains information about asynchronous operations. // // returns the result: // user-defined object that qualifies or contains information about asynchronous operations. object AsyncState { get; } / / / / abstract: / / get used to wait for the asynchronous operation to complete the System. Threading. WaitHandle. / / / / return results: / / used to wait for the asynchronous operation to complete the System. Threading. WaitHandle. WaitHandle AsyncWaitHandle { get; } // // Abstract: // Gets a value indicating whether the asynchronous operation has completed synchronously. // // Returns the following result: // True if the asynchronous operation completes synchronization; Otherwise, it is false. bool CompletedSynchronously { get; } // // Summary: // Gets a value indicating whether the asynchronous operation has completed. // // returns the result: // True if the operation completes, false otherwise. bool IsCompleted { get; }}Copy the code
- BeginInvoke returns IAsyncResult, which can be used to monitor the progress of the call.
- IAsyncResult is returned from the start operation and can be used to get the status of whether the asynchronous operation has completed.
- The result object is passed to the end operation, which returns the final return value of the call.
- Optional callbacks can be provided in the start operation. If a callback is provided, it will be invoked after the call ends; And the code in the callback can call the end operation.
- If you need to pass some additional information to the callback function, you put it in the third argument asyncState of the BeginInvoke() method. Note that this parameter is of type Object, so any type of data can be placed. If more than one message needs to be passed to the callback function, you can encapsulate all the messages to be passed into a Struct variable, or you can simply define a class that wraps the message into an object created by this class and passes it to the BeginInvoke() method.
The EndInvoke method is used to retrieve the result of an asynchronous call
Method declaration:
Public < method return value type >EndInvoke(< parameter declared as ref or out >, IAsyncResult result)Copy the code
- The result argument is returned by the BeginInvoke() method, which.NET uses to see if the method call has completed. \
- When the EndInvoke method finds that the asynchronous call is complete, it takes the return value of this asynchronously called method as its return value, and if the asynchronously called method has parameters declared as ref and out, it is also responsible for populating them. \
- The EndInvoke method can be called at any time after BeginInvoke is called. Note that EndInvoke is always called after the asynchronous call is complete. If the asynchronous call does not complete, EndInvoke blocks until the asynchronous call completes. \
- The parameters to EndInvoke include the out and ref parameters of the method that needs to be executed asynchronously and the IAsyncResult returned by BeginInvoke.
An instance of an asynchronous invocation
Take calculating the folder size as an example
API to calculate folder size
/// <param name="folderPath"> </param> /// <returns> Returns the size of the folder </returns> private static long CalculateFolderSize(string folderPath) { if (! Directory. The Exists (folderPath)) {throw new DirectoryNotFoundException (" folder path does not exist "); } long totalSize = 0; DirectoryInfo rootDir = new DirectoryInfo(folderPath); DirectoryInfo[] childDirs = rootdir.getDirectories (); FileInfo[] files = rootdir.getFiles (); Foreach (FileInfo file in files) {totalSize += file.length; } foreach (DirectoryInfo dir in childDirs) {totalSize += perform the same calculation foreach folder, adding up the size of all files under it CalculateFolderSize(dir.FullName); } return(totalSize); }Copy the code
Delegate definition that calculates the folder size
private delegate long CalculateFolderSizeDelegate(string folderPath);
Copy the code
Instantiate the delegate invocation
public FileOper() { _invoke = new CalculateFolderSizeDelegate(CalculateFolderSize); Public void BeginInvoke(string folderPath) {_invoke.BeginInvoke(folderPath, ShowFolderSize, folderPath); }Copy the code
Define the delegate callback function
//1.0 function for the callback //2.0 IAsyncResult is the return value passed to BeginInvoke after it executes. // BeginInvoke is returned at the start of the operation and returns a value of type IAsyncResult to monitor the status of the asynchronous call, so it automatically passes this value to the callback function. // IAsynCresult. AsyncState in the 3.0 callback corresponds to the third parameter of BeginInvoke, which is additional information. //4.0 Use the IsCompleted property of IAsyncResult to determine whether the asynchronous call is complete. // The first parameter of WaitOne is the number of milliseconds to wait for the asynchronous call to complete. When the call has not completed after the specified time, WaitOne returns false. // If the time is 0, it means no wait, and if -1, it means wait forever until the asynchronous call is complete. private void ShowFolderSize(IAsyncResult result) { try { long size = _invoke.EndInvoke(result); Console.WriteLine("\n folder {0} capacity: {1} bytes \n", (string)(result.asyncState), size); {} the catch (DirectoryNotFoundException e) Console. WriteLine (" folder path does not exist "); }}Copy the code
Test functions
string _folderName = @"D:\Software";
FileOper _fileOper = new FileOper();
while (true)
{
_fileOper.BeginInvoke(_folderName);
}
Copy the code
The test results
Core CS file
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; Namespace InvokeConsole {//1.0 asynchronous calls are made through delegates. // IAsyncResult RTN = Delegate variable. The BeginInvoke (...) ; // Start asynchronous calls // There are other things you can do in this section, the program is in asynchronous execution mode // Variables used to hold method results = delegate variables. EndInvoke(rtn); // block and wait for the asynchronous call to end /// <summary> /// file operation /// </summary> class FileOper {private Delegate Long CalculateFolderSizeDelegate(string folderPath); private CalculateFolderSizeDelegate _invoke = null; public FileOper() { _invoke = new CalculateFolderSizeDelegate(CalculateFolderSize); Public void BeginInvoke(string folderPath) {_invoke.BeginInvoke(folderPath, ShowFolderSize, folderPath); } //1.0 function for the callback //2.0 IAsyncResult is the return value passed to BeginInvoke after it executes. // BeginInvoke is returned at the start of the operation and returns a value of type IAsyncResult to monitor the status of the asynchronous call, so it automatically passes this value to the callback function. // IAsynCresult. AsyncState in the 3.0 callback corresponds to the third parameter of BeginInvoke, which is additional information. //4.0 Use the IsCompleted property of IAsyncResult to determine whether the asynchronous call is complete. // The first parameter of WaitOne is the number of milliseconds to wait for the asynchronous call to complete. When the call has not completed after the specified time, WaitOne returns false. // If the time is 0, it means no wait, and if -1, it means wait forever until the asynchronous call is complete. private void ShowFolderSize(IAsyncResult result) { try { long size = _invoke.EndInvoke(result); Console.WriteLine("\n folder {0} capacity: {1} bytes \n", (string)(result.asyncState), size); {} the catch (DirectoryNotFoundException e) Console. WriteLine (" folder path does not exist "); }} / / / < summary > / / / to calculate the size of the folder / / / < summary > / / / < param name = "folderPath" > folder of the full path < param > / / / <returns> Return folder size </returns> private static long CalculateFolderSize(string folderPath) {if (! Directory. The Exists (folderPath)) {throw new DirectoryNotFoundException (" folder path does not exist "); } long totalSize = 0; DirectoryInfo rootDir = new DirectoryInfo(folderPath); DirectoryInfo[] childDirs = rootdir.getDirectories (); FileInfo[] files = rootdir.getFiles (); Foreach (FileInfo file in files) {totalSize += file.length; } foreach (DirectoryInfo dir in childDirs) {totalSize += perform the same calculation foreach folder, adding up the size of all files under it CalculateFolderSize(dir.FullName); } return(totalSize); }}}Copy the code