Avant-propos
Récemment, je lisais le livre "C# Concurrent Programming·Classic Exemples". Ce n'est pas un livre théorique, mais c'est un livre qui parle principalement de la façon de mieux utiliser le C#.NET actuel. Un livre de ces API nous est fourni. La plupart des livres sont des exemples, qui sont souvent utilisés dans le développement quotidien.
Certains points de vue exprimés dans le livre sont tout à fait agréables. Par exemple, l'auteur a déclaré que la plupart des livres actuels mettent du contenu sur le multithreading simultané à la fin, mais manquent d'un guide d'introduction et d'une référence pour la programmation simultanée. .
Un autre point de vue est que la grande majorité du personnel technique national pense que plus le niveau de technologie est bas, plus elle est puissante, tandis que ceux qui réalisent des applications de niveau supérieur sont des « agriculteurs de code ». s'oppose à ce point de vue. En fait, nous pouvons faire bon usage de la bibliothèque existante, c'est aussi une sorte de capacité. Bien que comprendre les connaissances de base soit toujours utile dans la vie quotidienne, il est préférable d'apprendre des concepts abstraits de niveau supérieur.
Bases de l'asynchrone
Suspension et hibernation des tâches
Pour suspendre ou mettre en veille prolongée des tâches de manière asynchrone, vous pouvez utiliser Task.Delay();
static async Task<T> DelayResult<T>(T result, TimeSpan delay) { await Task.Delay(delay); return result; }
Asynchrone redémarrer Mécanisme d'essai
Une stratégie d'attente exponentielle simple, le temps de nouvelle tentative augmentera progressivement. Cette stratégie est généralement utilisée lors de l'accès aux services Web.
static async Task<string> DownloadString(string uri) { using (var client = new HttpClient()) { var nextDealy = TimeSpan.FromSeconds(1); for (int i = 0; i != 3; ++i) { try { return await client.GetStringAsync(uri); } catch { } await Task.Delay(nextDealy); nextDealy = nextDealy + nextDealy; } //最后重试一次,抛出出错信息 return await client.GetStringAsync(uri); } }
Rapport de progression
Dans les opérations asynchrones, il est souvent nécessaire d'afficher la progression de l'opération, et vous pouvez utiliser IProcess
static async Task MyMethodAsync(IProgress<double> progress) { double precentComplete = 0; bool done = false; while (!done) { await Task.Delay(100); if (progress != null) { progress.Report(precentComplete); } precentComplete++; if (precentComplete == 100) { done = true; } } } public static void Main(string[] args) { Console.WriteLine("starting..."); var progress = new Progress<double>(); progress.ProgressChanged += (sender, e) => { Console.WriteLine(e); }; MyMethodAsync(progress).Wait(); Console.WriteLine("finished"); }
Attendez un groupe de tâches
Exécutez plusieurs tâches en même temps et attendez qu'elles soient toutes terminées
Task task1 = Task.Delay(TimeSpan.FromSeconds(1)); Task task2 = Task.Delay(TimeSpan.FromSeconds(2)); Task task3 = Task.Delay(TimeSpan.FromSeconds(1)); Task.WhenAll(task1, task2, task3).Wait();
Attendez qu'une tâche se termine terminer
Exécuter plusieurs tâches, il suffit de répondre à l'achèvement de l'une d'entre elles. Il est principalement utilisé pour effectuer plusieurs tentatives indépendantes sur une opération. Tant qu'une des tentatives est terminée, la tâche est terminée.
static async Task<int> FirstResponseUrlAsync(string urlA, string urlB) { var httpClient = new HttpClient(); Task<byte[]> downloadTaskA = httpClient.GetByteArrayAsync(urlA); Task<byte[]> downloadTaskB = httpClient.GetByteArrayAsync(urlB); Task<byte[]> completedTask = await Task.WhenAny(downloadTaskA, downloadTaskB); byte[] data = await completedTask; return data.Length; }
Collections
Piles et files d'attente immuables
Nécessite une pile et une file d'attente qui ne seront pas modifiées fréquemment et sont accessibles en toute sécurité par plusieurs threads. Leur API est très similaire à Stack
En implémentation interne, lorsqu'un objet est écrasé (réaffecté), la collection immuable renvoie une collection modifiée, et la référence de la collection d'origine ne change pas, c'est-à-dire si une autre variable fait référence à la même. objet, il (les autres variables) ne changera pas.
ImmutableStack
var stack = ImmutableStack<int>.Empty; stack = stack.Push(11); var biggerstack = stack.Push(12); foreach (var item in biggerstack) { Console.WriteLine(item); } // output: 12 11 int lastItem; stack = stack.Pop(out lastItem); Console.WriteLine(lastItem); //output: 11
En fait, les deux piles partagent la mémoire de stockage 11 en interne. Cette implémentation est très efficace et chaque instance est thread-safe.
ImmutableQueue
var queue = ImmutableQueue<int>.Empty; queue = queue.Enqueue(11); queue = queue.Enqueue(12); foreach (var item in queue) { Console.WriteLine(item); } // output: 11 12 int nextItem; queue = queue.Dequeue(out nextItem); Console.WriteLine(nextItem); //output: 11
Listes et collections immuables
ImmutableList
Complexité temporelle
Parfois, vous avez besoin d'une structure de données qui prend en charge l'indexation, qui n'est pas modifiée fréquemment et qui est accessible en toute sécurité par plusieurs threads.
var list = ImmutableList<int>.Empty; list = list.Insert(0, 11); list = list.Insert(0, 12); foreach (var item in list) { Console.WriteLine(item); } // 12 11
ImmutableList
ImmutableHashSet
Parfois, une telle structure de données est nécessaire : elle n'a pas besoin de stocker du contenu en double, n'est pas modifiée fréquemment et est accessible en toute sécurité par plusieurs threads. Complexité temporelle O(log N).
var set = ImmutableHashSet<int>.Empty; set = set.Add(11); set = set.Add(12); foreach (var item in set) { Console.WriteLine(item); } // 11 12 顺序不定
Dictionnaire thread-safe
Une collection de paires clé-valeur thread-safe, plusieurs threads peuvent toujours maintenir la synchronisation lors de la lecture et de l'écriture.
ConcurrentDictionary
utilise un mélange de techniques de verrouillage à grain fin et de techniques sans verrouillage, et c'est l'un des types de collection les plus pratiques.
var dictionary = new ConcurrentDictionary<int, string>(); dictionary.AddOrUpdate(0, key => "Zero", (key, oldValue) => "Zero");
Si plusieurs threads lisent et écrivent une collection partagée, l'utilitaire ConcurrentDictionary
Il est particulièrement adapté aux situations où les données doivent être partagées, c'est-à-dire que plusieurs threads partagent une collection. Si certains threads ajoutent uniquement des éléments et certains threads suppriment uniquement des éléments, il est préférable d'utiliser un producteur/consommateur. collection ( BlockingCollection
Initialiser les ressources partagées
Le programme utilise une valeur à plusieurs endroits et l'initialise lors du premier accès.
static int _simpleVluae; static readonly Lazy<Task<int>> shardAsyncInteger = new Lazy<Task<int>>(async () => { await Task.Delay(2000).ConfigureAwait(false); return _simpleVluae++; }); public static void Main(string[] args) { int shareValue = shardAsyncInteger.Value.Result; Console.WriteLine(shareValue); // 0 shareValue = shardAsyncInteger.Value.Result; Console.WriteLine(shareValue); // 0 shareValue = shardAsyncInteger.Value.Result; Console.WriteLine(shareValue); // 0 }
Ce qui précède est le contenu des notes de lecture des exemples classiques de programmation simultanée C#. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !