C# a une histoire de plus de 10 ans. À en juger par les progrès de mise à jour de Microsoft tous les deux ans, la vitalité est extrêmement forte. La programmation asynchrone en C# a également connu l'évolution de plusieurs versions, j'écrirai un. série d'articles de blog à enregistrer Jetons un coup d'œil à l'historique du développement de la programmation asynchrone en C#. Publicité : Amis qui aiment mon article, veuillez cliquer sur "Suivez-moi" ci-dessous. Merci
Je suis entré en contact et j'ai utilisé C# en 2004. La version C# était 1.1 à cette époque, nous commencerons donc à parler à partir de ce moment-là. A cette époque, je lisais et écrivais des programmes moi-même à l'université. La plupart des programmes que j'écrivais étaient des programmes synchrones, et au plus un thread pouvait être démarré... En fait, à l'ère de C# 1.1, il y avait un programme complet. solution de programmation asynchrone, et c'était APM (modèle de programmation asynchrone). Si vous ne comprenez toujours pas « programmes synchrones et programmes asynchrones », veuillez vous rendre sur Baidu.
La caractéristique la plus représentative du modèle de programmation asynchrone APM est qu'une fonction asynchrone se compose de deux méthodes commençant par Begin et End. La méthode commençant par Begin signifie démarrer l'exécution de la fonction asynchrone, et la méthode commençant par End signifie attendre que la fonction asynchrone termine son exécution et renvoie le résultat de l'exécution. Ce qui suit est une implémentation de simulation (l'implémentation asynchrone du modèle APM standard sera écrite plus tard) :
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(); }
Dans le modèle APM de simulation ci-dessus, nous avons utilisé Thread et ManualResetEvent. threading Si vous n'êtes pas familier avec ManualResetEvent, l'utilisation de la programmation asynchrone en C# impliquera inévitablement des connaissances multi-threading. Bien que Microsoft ait fait beaucoup d'encapsulation dans le Framework, les amis devraient en saisir l'essence.
La raison pour laquelle le modèle asynchrone APM simulé ci-dessus est simple est que de nombreuses excellentes règles de syntaxe ont été introduites au cours du processus de développement de C#. Dans l'exemple ci-dessus, nous avons davantage utilisé les expressions Lambda. Si vous n'êtes pas familier avec les délégués anonymes et les expressions lambda, vous pouvez lire mon précédent Bolg "Délégués anonymes et expressions Lambda". Il y a tellement de publicités ci-dessus, voyons comment le modèle APM standard implémente la programmation asynchrone.
Interface IAsyncResult
L'interface IAsyncResult définit l'état des fonctions asynchrones. Les attributs et significations spécifiques de cette interface sont les suivants :
// 表示异步操作的状态。 [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; } }
Remarque : L'exemple de modèle ManualResetEvent en 1 hérite de WaitHandle<br/>Méthode d'implémentation de la légende APM<br/>Après avoir compris l'interface IAsyncResult, nous terminerons la réécriture de l'exemple de simulation en implémentant l'interface IAsyncResult. Le code est le suivant :
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; } }
Exemple d'analyse de code :
Dans le code ci-dessus, la classe interne de NewWorker WorkerAsyncResult est le point clé. Elle implémente l'interface IAsyncResult et est responsable du démarrage d'un nouveau thread pour terminer le travail de calcul.
Deux attributs privés A et B sont ajoutés à WorkerAsyncResult pour stocker les valeurs utilisées pour le calcul, et un attribut R lisible en externe mais non inscriptible est utilisé pour stocker les résultats du fonctionnement interne de WorkerAsyncResult. La propriété AsyncWaitHandle est sollicitée par un ManualResetEvent, et un ManualResetEvent est créé (mais non publié) lors du premier accès. Les autres propriétés de l'interface sont implémentées normalement, il n'y a rien à dire.
Une nouvelle méthode Count statique est ajoutée à WorkerAsyncResult. L'état du paramètre est l'objet WorkerAsyncResult actuel qui appelle la méthode Count. La méthode Count s'exécute dans le thread nouvellement démarré de l'objet WorkerAsyncResult, donc Thread.Sleep(1000) bloquera le nouveau thread pendant 1 seconde. Définissez ensuite si l'objet WorkerAsyncResult actuel est terminé de manière synchrone sur false et si l'état d'achèvement asynchrone est vrai. Libérez la notification ManualResetEvent pour attendre que le thread reçoive la notification et entre dans l'état d'exécution. Déterminez s'il existe un délégué de rappel de fin d'exécution asynchrone. , et rappelez-le s'il existe.
NewWorker est très simple. Les deux attributs Num1 et Num2 sont les valeurs à calculer. BeginWork crée un objet WorkerAsyncResult et transmet les deux valeurs à calculer, Num1, Num2, le délégué de rappel userCallback et le type d'objet asyncState, dans l'objet WorkerAsyncResult à créer. Après cette étape, l'objet WorkerAsyncResult obtient toutes les données requises pour l'opération, le rappel une fois l'opération terminée et démarre immédiatement un nouveau thread pour effectuer l'opération (exécuter la méthode WorkerAsyncResult.Count).
Étant donné que WorkerAsyncResult.Count est exécuté dans un nouveau thread, l'état du nouveau thread ne peut pas être connu avec précision en dehors du thread. Afin de répondre au besoin de synchronisation des threads externes avec les nouveaux threads, la méthode EndWork est ajoutée à NewWorker et le type de paramètre est IAsyncResult. Pour appeler la méthode EndWork, vous devez transmettre l'objet WorkerAsyncResult obtenu par BeginWork. Une fois que la méthode EndWork a obtenu l'objet WorkerAsyncResult, appelez la méthode WorkerAsyncResult.AsyncWaitHandle.WaitOne() et attendez la notification ManualResetEvent lorsque la notification est obtenue. Le thread d'opération a terminé l'opération (le thread n'est pas terminé), l'étape suivante consiste à obtenir le résultat de l'opération R et à le renvoyer.
Vient ensuite le programme d'appel NewWorker, comme suit :
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(); }
Le processus d'appel du programme que j'ai simplement dessiné dans la figure ci-dessous vous aidera à comprendre :
La programmation asynchrone du modèle APM standard est trop compliquée pour les développeurs. Par conséquent, la programmation asynchrone via l'implémentation de l'interface IAsyncResult est le de la légende qui a l'air bien mais qui n'est pas utile (sin, sin, sin...).
Déléguer la programmation asynchrone (implémentation de la norme APM)
C#中委托天生支持异步调用(APM模型),任何委托对象后"."就会发现BeginInvoke、EndInvoke、Invoke三个方法。BeginInvoke为异步方式调用委托、EndInvoke等待委托的异步调用结束、Invoke同步方式调用委托。因此上面的标准APM实例,可借助 delegate 进行如下简化。
上面NewWorker使用委托方式改写如下:
<br/>
<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(); }
上面的使用委托进行APM异步编程,比实现 IAsyncResult 接口的方式精简太多、更易理解使用。因此这里建议朋友们 delegate 异步调用模型应该掌握起来,而通过实现 IAsyncResult 接口的传说方式看你的喜好吧。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!