C# has a history of more than 10 years. Judging from Microsoft’s update progress every two years, the vitality is extremely strong. Asynchronous programming in C# has also experienced the evolution of multiple versions. Starting today, I will write a series of blog posts to record Let’s take a look at the development history of asynchronous programming in C#. Advertisement: Friends who like my article, please click "Follow Me" below. Thank you
I came into contact with and used C# in 2004. The C# version was 1.1 at that time, so we will start talking from that time. At that time, I was reading and writing programs by myself in college. Most of the programs I wrote were synchronous programs, and at most one thread could be started... In fact, in the era of C# 1.1, there was a complete asynchronous programming solution, and that was APM. (Asynchronous programming model). If you still don’t understand “synchronous programs and asynchronous programs”, please go to Baidu.
The most representative feature of the APM asynchronous programming model is that an asynchronous function consists of two methods starting with Begin and End. The method starting with Begin means starting the execution of the asynchronous function, and the method starting with End means waiting for the asynchronous function to finish executing and returning the execution result. The following is a simulation implementation (the standard APM model asynchronous implementation will be written later):
public class Worker { public int A { get; set; } public int B { get; set; } private int R { get; set; } ManualResetEvent et; public void BeginWork(Action action) { et = new ManualResetEvent(false); new Thread(() => { R = A + B; Thread.Sleep(1000); et.Set(); if(null != action) { action(); } }).Start(); } public int EndWork() { if(null == et) { t hrow new Exception("调用EndWork前,需要先调用BeginWork"); } else { et.WaitOne(); return R; } } }
static void Main(string[] args) { Worker w = new Worker(); w.BeginWork(()=> { Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId, w.EndWork()); }); Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); }
In the above simulation APM model we used Thread and ManualResetEvent. If you are not familiar with multi-threading and ManualResetEvent, C# The use of asynchronous programming will inevitably involve the knowledge of multi-threading. Although Microsoft has done a lot of encapsulation in Framework, friends should grasp its essence.
The reason why the APM asynchronous model simulated above is simple is because many excellent grammar rules were introduced during the development process of C#. In the above example, we used Lambda expressions more. If you are not familiar with anonymous delegates and lambda expressions, you can read my previous Bolg "Anonymous Delegates and Lambda Expressions". There are so many advertisements above, let's take a look at how the standard APM model implements asynchronous programming.
IAsyncResult interface
IAsyncResult interface defines the status of asynchronous functions. The specific attributes and meanings of this interface are as follows:
// 表示异步操作的状态。 [ComVisible(true)] public interface IAsyncResult { // // 摘要: // 获取一个值,该值指示异步操作是否已完成。 // // 返回结果: // 如果操作已完成,则为 true;否则为 false。 bool IsCompleted { get; } // // 摘要: // 获取用于等待异步操作完成的 System.Threading.WaitHandle。 // // 返回结果: // 用于等待异步操作完成的 System.Threading.WaitHandle。 WaitHandle AsyncWaitHandle { get; } // // 摘要: // 获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。 // // 返回结果: // 一个用户定义的对象,限定或包含有关异步操作的信息。 object AsyncState { get; } // // 摘要: // 获取一个值,该值指示异步操作是否同步完成。 // // 返回结果: // 如果异步操作同步完成,则为 true;否则为 false。 bool CompletedSynchronously { get; } }
Note: In model example 1 The ManualResetEvent inherits from WaitHandle<br/>APM Legend Implementation Method<br/>After understanding the IAsyncResult interface, we will complete the rewriting of the simulation example by implementing the IAsyncResult interface. The code is as follows:
public class NewWorker { public class WorkerAsyncResult : IAsyncResult { AsyncCallback callback; public WorkerAsyncResult(int a,int b, AsyncCallback callback, object asyncState) { A = a; B = b; state = asyncState; this.callback = callback; new Thread(Count).Start(this); } public int A { get; set; } public int B { get; set; } public int R { get; private set; } private object state; public object AsyncState { get { return state; } } private ManualResetEvent waitHandle; public WaitHandle AsyncWaitHandle { get { if (null == waitHandle) { waitHandle = new ManualResetEvent(false); } return waitHandle; } } private bool completedSynchronously; public bool CompletedSynchronously { get { return completedSynchronously; } } private bool isCompleted; public bool IsCompleted { get { return isCompleted; } } private static void Count(object state) { var result = state as WorkerAsyncResult; result.R = result.A + result.B; Thread.Sleep(1000); result.completedSynchronously = false; result.isCompleted = true; ((ManualResetEvent)result.AsyncWaitHandle).Set(); if (result.callback != null) { result.callback(result); } } } public int Num1 { get; set; } public int Num2 { get; set; } public IAsyncResult BeginWork(AsyncCallback userCallback, object asyncState) { IAsyncResult result = new WorkerAsyncResult(Num1,Num2,userCallback, asyncState); return result; } public int EndWork(IAsyncResult result) { WorkerAsyncResult r = result as WorkerAsyncResult; r.AsyncWaitHandle.WaitOne(); return r.R; } }
Sample code Analysis:
The internal class WorkerAsyncResult of NewWorker in the above code is the key point. It implements the IAsyncResult interface and is responsible for starting a new thread to complete the calculation work.
In WorkerAsyncResult, two private properties A and B are added to store the values used for calculation, and an externally readable and non-writable property R is used to store the results of the internal operation of WorkerAsyncResult. The AsyncWaitHandle property is acted upon by a ManualResetEvent, and a ManualResetEvent is created (but not released) on first access. Other interface properties are implemented normally, there is nothing to say.
There is a new static Count method in WorkerAsyncResult, and the parameter state is the current WorkerAsyncResult object that calls the Count method. The Count method runs in the newly started thread of the WorkerAsyncResult object, so Thread.Sleep(1000) will block the new thread for 1 second. Then set whether the current WorkerAsyncResult object is synchronously completed to false, and the asynchronous completion status is true. Release the ManualResetEvent notification to wait for the thread to get the notification and enter the execution state. Determine whether there is an asynchronous execution end callback delegate, and call it back if it exists.
NewWorker is very simple. The two properties Num1 and Num2 are the values to be calculated. BeginWork creates a WorkerAsyncResult object, and passes the two values to be calculated, Num1, Num2, userCallback callback delegate, and object type asyncState, into the WorkerAsyncResult object to be created. After this step, the WorkerAsyncResult object obtains all the data required for the operation, the callback after the operation is completed, and immediately starts a new thread to perform the operation (execute the WorkerAsyncResult.Count method).
Because WorkerAsyncResult.Count is executed in a new thread, the status of the new thread cannot be accurately known outside the thread. In order to meet the need for external threads to synchronize with new threads, the EndWork method is added to NewWorker, and the parameter type is IAsyncResult. To call the EndWork method, you should pass in the WorkerAsyncResult object obtained by BeginWork. After the EndWork method obtains the WorkerAsyncResult object, call the WorkerAsyncResult.AsyncWaitHandle.WaitOne() method and wait for the ManualResetEvent notification. When the notification is obtained, the operation thread has completed the operation (the thread has not ended ), the next step is to obtain the operation result R and return it.
Next is the NewWorker calling program, as follows:
static void Main(string[] args) { NewWorker w2 = new NewWorker(); w2.Num1 = 10; w2.Num2 = 12; IAsyncResult r = null; r = w2.BeginWork((obj) => { Console.WriteLine("Thread Id:{0},Count:{1}",Thread.CurrentThread.ManagedThreadId, w2.EndWork(r)); }, null); Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); }
The following picture is a simple drawing of the program calling process, which will help friends understand:
The standard APM model asynchronous programming is too complicated for developers. Therefore, asynchronous programming by implementing the IAsyncResult interface is the in the legend.
Delegate asynchronous programming (APM standard implementation)C#中委托天生支持异步调用(APM模型),任何委托对象后"."就会发现BeginInvoke、EndInvoke、Invoke三个方法。BeginInvoke为异步方式调用委托、EndInvoke等待委托的异步调用结束、Invoke同步方式调用委托。因此上面的标准APM实例,可借助 delegate 进行如下简化。 上面NewWorker使用委托方式改写如下: <br/> 调用程序: 上面的使用委托进行APM异步编程,比实现 IAsyncResult 接口的方式精简太多、更易理解使用。因此这里建议朋友们 delegate 异步调用模型应该掌握起来,而通过实现 IAsyncResult 接口的传说方式看你的喜好吧。 The above is the detailed content of C# asynchronous APM mode asynchronous program development example sharing. For more information, please follow other related articles on the PHP Chinese website!<br/>
public class NewWorker2
{
Func<int, int, int> action; public NewWorker2()
{
action = new Func<int, int, int>(Work);
} public IAsyncResult BeginWork(AsyncCallback callback, object state)
{ dynamic obj = state; return action.BeginInvoke(obj.A, obj.B, callback, this);
} public int EndWork(IAsyncResult asyncResult)
{ try
{ return action.EndInvoke(asyncResult);
} catch (Exception ex)
{ throw ex;
}
} private int Work(int a, int b)
{
Thread.Sleep(1000); return a + b;
}
}
static void Main(string[] args)
{
NewWorker2 w2 = new NewWorker2();
IAsyncResult r = null;
r = w2.BeginWork((obj) =>
{
Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId,
w2.EndWork(r));
}, new { A = 10, B = 11 });
Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
}