Heim  >  Artikel  >  Backend-Entwicklung  >  C#-Parallelprogrammierung · Klassische Beispiele Lesenotizen

C#-Parallelprogrammierung · Klassische Beispiele Lesenotizen

黄舟
黄舟Original
2017-02-06 16:43:461448Durchsuche

Vorwort

Kürzlich habe ich das Buch „C# Concurrent Programming·Classic Examples“ gelesen. Dies ist kein theoretisches Buch, sondern ein Buch, das hauptsächlich darüber spricht, wie man das aktuelle C#.NET besser nutzen kann Ein Buch dieser APIs wird uns zur Verfügung gestellt. Bei den meisten Büchern handelt es sich um Beispiele, die häufig in der täglichen Entwicklung verwendet werden.

Einige der Ansichten im Buch sind durchaus erfreulich. Beispielsweise sagte der Autor, dass die meisten aktuellen Bücher Inhalte zum gleichzeitigen Multithreading am Ende platzieren, es aber an einer Einführung und Referenz für gleichzeitiges Programmieren mangelt .

Ein anderer Standpunkt ist, dass die überwiegende Mehrheit des inländischen technischen Personals glaubt, dass die Technologie umso leistungsfähiger ist, je niedriger sie ist, während diejenigen, die Anwendungen auf höherer Ebene ausführen, „Code-Farmer“ sind widersetzt sich dieser Ansicht. Tatsächlich ist es auch eine Art Fähigkeit, die vorhandene Bibliothek gut zu nutzen. Obwohl das Verständnis des Grundwissens im täglichen Leben immer noch hilfreich ist, ist es besser, aus abstrakten Konzepten auf höherer Ebene zu lernen.

Asynchrone Grundlagen

Aufgaben anhalten und in den Ruhezustand versetzen

Um Aufgaben asynchron anzuhalten oder in den Ruhezustand zu versetzen, können Sie Task.Delay();

static async Task<T> DelayResult<T>(T result, TimeSpan delay) {
    await Task.Delay(delay);
    return result;
}

Asynchron verwenden Neustart-Testmechanismus

Eine einfache exponentielle Backoff-Strategie, die Wiederholungszeit erhöht sich allmählich. Diese Strategie wird im Allgemeinen beim Zugriff auf Webdienste verwendet.

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);
    }
}

Fortschritt melden

Bei asynchronen Vorgängen ist es oft notwendig, den Fortschritt des Vorgangs anzuzeigen, und Sie können IProcess verwenden.

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");
}

Warten Sie auf eine Gruppe von Aufgaben

Führen Sie mehrere Aufgaben gleichzeitig aus und warten Sie, bis sie alle abgeschlossen sind

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();

Warten Sie, bis eine Aufgabe abgeschlossen ist abgeschlossen

Mehrere Aufgaben ausführen, Sie müssen nur auf den Abschluss einer davon reagieren. Es wird hauptsächlich verwendet, um mehrere unabhängige Versuche für einen Vorgang durchzuführen. Solange einer der Versuche abgeschlossen ist, ist die Aufgabe abgeschlossen.

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;
}

Sammlungen

Unveränderliche Stapel und Warteschlangen

Erfordern einen Stapel und eine Warteschlange, die nicht häufig geändert werden und auf die mehrere Threads sicher zugreifen können. Ihre API ist Stack sehr ähnlich. In Bezug auf die Leistung weisen unveränderliche Stacks (LIFO) und Warteschlangen (FIFO) die gleiche zeitliche Komplexität auf wie Standard-Stacks und -Warteschlangen. Aber in einfachen Fällen, in denen häufige Änderungen erforderlich sind, sind Standard-Stacks und -Warteschlangen schneller.

Wenn in der internen Implementierung ein Objekt überschrieben (neu zugewiesen) wird, gibt die unveränderliche Sammlung eine geänderte Sammlung zurück und die ursprüngliche Sammlungsreferenz ändert sich nicht. Das heißt, wenn eine andere Variable auf dasselbe verweist Objekt, es (andere Variablen) wird sich nicht ändern.

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

Tatsächlich teilen sich die beiden Stapel den internen Speicher von 11. Diese Implementierung ist sehr effizient und jede Instanz ist threadsicher.

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

Unveränderliche Listen und Sammlungen

ImmutableList

Zeitkomplexität

C#-Parallelprogrammierung · Klassische Beispiele Lesenotizen

Manchmal benötigen Sie eine Datenstruktur, die die Indizierung unterstützt, nicht häufig geändert wird und auf die mehrere Threads sicher zugreifen können.

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 kann indiziert werden, aber achten Sie auf Leistungsprobleme und können Sie es nicht einfach zum Ersetzen von List verwenden. Seine interne Implementierung verwendet einen Binärbaum zum Organisieren von Daten. Dies geschieht, um die gemeinsame Nutzung von Speicher zwischen verschiedenen Instanzen zu ermöglichen.

ImmutableHashSet

Manchmal ist eine solche Datenstruktur erforderlich: Sie muss keinen doppelten Inhalt speichern, wird nicht häufig geändert und kann von mehreren Threads sicher aufgerufen werden. Zeitkomplexität 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 顺序不定

Thread-sicheres Wörterbuch

Eine thread-sichere Schlüssel-Wert-Paar-Sammlung, mehrere Threads können beim Lesen und Schreiben weiterhin die Synchronisation aufrechterhalten.

ConcurrentDictionary

verwendet eine Mischung aus feinkörnigen Sperrtechniken und sperrfreien Techniken und ist einer der praktischsten Sammlungstypen.

var dictionary = new ConcurrentDictionary<int, string>();
dictionary.AddOrUpdate(0, key => "Zero", (key, oldValue) => "Zero");

Wenn mehrere Threads eine gemeinsame Sammlung lesen und schreiben, ist das Dienstprogramm ConcurrentDictionary am besten geeignet. Wenn es nicht häufig geändert wird, ist die Verwendung von ImmutableDictionary besser geeignet.

Es eignet sich am besten für Situationen, in denen Daten gemeinsam genutzt werden müssen, d. h. mehrere Threads teilen sich eine Sammlung. Wenn einige Threads nur Elemente hinzufügen und andere Threads nur Elemente entfernen, ist es am besten, einen Produzenten/Konsumenten zu verwenden Sammlung (BlockingCollection).

Gemeinsame Ressourcen initialisieren

Das Programm verwendet einen Wert an mehreren Stellen und initialisiert ihn beim ersten Zugriff darauf.

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
}

Das Obige ist der Inhalt der Lesehinweise für C# Concurrent Programming·Classic-Beispiele. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn