Maison  >  Article  >  développement back-end  >  Programmation simultanée C# · Exemples classiques Notes de lecture

Programmation simultanée C# · Exemples classiques Notes de lecture

黄舟
黄舟original
2017-02-06 16:43:461461parcourir

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 et Process.

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 et Queue. En termes de performances, les piles et files d'attente immuables (LIFO) et les files d'attente (FIFO) ont la même complexité temporelle que les piles et files d'attente standard. Mais dans les cas simples où des modifications fréquentes sont nécessaires, les piles et files d’attente standard sont plus rapides.

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

Programmation simultanée C# · Exemples classiques Notes de lecture

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 peut être indexé, mais faites attention aux problèmes de performances et ne pouvez pas l'utiliser pour simplement remplacer List. Son implémentation interne utilise un arbre binaire pour organiser les données afin de permettre le partage de la mémoire entre différentes instances.

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 S'il n'est pas modifié fréquemment, il est plus approprié d'utiliser ImmutableDictionary.

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) !


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn