Nehmen wir an, ich habe dieses einfache Snippet:
%Vor%Offensichtlich wird jedes Mal, wenn ich diesen Knopf drücke, eine neue Aufgabe gestartet, selbst wenn eine vorherige Aufgabe noch läuft. Wie würde ich eine neue Aufgabe verschieben, bis alle vorherigen Aufgaben beendet sind?
Einige weitere Details:
Im obigen Beispiel ist jede neue Aufgabe identisch mit der vorherigen Aufgabe. Im ursprünglichen Kontext spielt jedoch die Abfolge der Aufgaben eine Rolle: Die Parameter können sich ändern (ich könnte sie mit DateTime.Now.Ticks
"simulieren").
Die Aufgaben sollten in der Reihenfolge ausgeführt werden, in der sie "registriert" sind. Speziell wird mein Programm mit einem seriellen Gerät sprechen. Ich habe das schon mal mit einem Hintergrund-Thread gemacht, der ein BlockingCollection
benutzt. Dieses Mal gibt es jedoch ein striktes Anfrage / Antwort-Protokoll und ich möchte async / wait verwenden, wenn es möglich ist.
Mögliche Lösung:
Ich könnte mir vorstellen, Aufgaben zu erstellen und sie in einer Liste zu speichern. Aber wie würde ich die Aufgaben in Bezug auf die Anforderungen ausführen? Oder sollte ich zu der Thread-basierten Lösung zurückkehren, die ich zuvor verwendet habe?
Sie könnten auf ein SemaphoreSlim
asynchron warten und es freigeben, sobald der Job fertig ist. Vergessen Sie nicht, die Semaphor-Anfangszahl auf 1
zu konfigurieren.
Ich empfehle die Verwendung von SemaphoreSlim
für die Synchronisation. Sie möchten jedoch vermeiden Task.Factory.StartNew
(wie ich auf meinem Blog erkläre), und auch definitiv vermeide async void
(wie ich im MSDN Artikel).
Wie wäre es mit dem Dataflow.ActionBlock<T>
mit dem (voreingestellten) maximalen Parallelitätsgrad von 1.? Auf diese Weise müssen Sie sich nicht um die Threadsicherheit / Sperrprobleme kümmern.
Es könnte etwa so aussehen:
%Vor% Sie können den ActionBlock auch so einrichten, dass er ein Task
oder Func<Task>
erhält und einfach / diese Eingabe ausführen. Dadurch könnten mehrere Vorgänge aus verschiedenen Quellen in die Warteschlange gestellt und abgewartet werden.
Ich könnte etwas übersehen haben, aber ich denke nicht, dass SemaphoreSlim
für das OP-Szenario benötigt wird. Ich würde es so machen. Im Grunde ist der Code nur await
der vorherigen ausstehenden Instanz der Aufgabe vor dem Fortfahren (keine Ausnahmebehandlung für Klarheit):
[UPDATE] Um den Kommentar zu adressieren, ist hier eine Thread-sichere Version, wenn button_Click
gleichzeitig aufgerufen werden kann:
Tags und Links c# async-await asynchronous