.NET ThreadPool QueueUserWorkItem Synchronisation

8

Ich verwende ThreadPool.QueueUserWorkItem, um einige Audiodateien abzuspielen und dabei die GUI nicht aufzuhängen.

Es funktioniert, hat aber eine unerwünschte Nebenwirkung.

Während das QueueUserWorkItem CallBack Proc ausgeführt wird, gibt es nichts, was es daran hindern könnte, einen neuen Thread zu starten. Dies führt dazu, dass sich die Samples in den Threads überlappen.

Wie kann ich es so einrichten, dass es darauf wartet, dass der bereits laufende Thread beendet wird und erst dann die nächste Anfrage ausführt?

BEARBEITEN: private object sync = new Object(); lock (sync) { .......do sound here }

das funktioniert. spielt in den Tönen der Reihe nach.

aber einige Samples werden mehr als einmal abgespielt, wenn ich Sound-Anfragen weitergebe, bevor das gespielte endet. wird untersuchen.

EDIT: Ist das obige ein Ergebnis von Lock Convoy @Aaronaught erwähnt?

    
iTEgg 28.03.2010, 18:17
quelle

8 Antworten

7

Dies ist ein klassisches Thread-Synchronisierungsproblem, bei dem Sie mehrere Clients haben, die alle die gleiche Ressource verwenden möchten und kontrollieren müssen, wie sie darauf zugreifen. In diesem speziellen Fall ist das Soundsystem bereit, mehr als einen Sound gleichzeitig zu spielen (und das ist oft wünschenswert), aber da Sie dieses Verhalten nicht möchten, können Sie das Gate-System mit Standard-Locking-Funktionen versehen :

%Vor%

Dabei steht Sound für jedes Objekt, das Sie für die Wiedergabe eines Sounds verwenden. Dieser Mechanismus ist "first come, first served", wenn mehrere Sounds um Spielzeit wetteifern.

Wie in einer anderen Antwort erwähnt, seien Sie vorsichtig mit übermäßigen Anhäufungen von Geräuschen, da Sie damit beginnen, den ThreadPool zu binden.

    
Dan Bryant 28.03.2010, 19:11
quelle
6

Sie können einen einzelnen Thread mit einer Warteschlange verwenden, um alle Sounds abzuspielen.

Wenn Sie einen Sound abspielen möchten, fügen Sie eine Anfrage in die Warteschlange ein und signalisieren dem spielenden Thread, dass eine neue Sounddatei abgespielt werden muss. Der Sound-Spiel-Thread sieht die neue Anfrage und spielt sie ab. Sobald der Sound abgeschlossen ist, überprüft er, ob weitere Sounds in der Warteschlange sind und wenn dies der Fall ist, wartet er auf die nächste Anfrage.

Ein mögliches Problem bei dieser Methode besteht darin, dass Sie, wenn Sie zu viele Sounds haben, die gespielt werden müssen, einen ständig wachsenden Rückstand bekommen, so dass die Sounds einige Sekunden oder möglicherweise sogar Minuten später kommen. Um dies zu vermeiden, sollten Sie die Warteschlangengröße begrenzen und einige Sounds löschen, wenn Sie zu viele haben.

    
Mark Byers 28.03.2010 18:25
quelle
2

Eine sehr einfache Producer / Consumer-Warteschlange wäre hier ideal - da Sie nur einen Producer und einen Consumer haben, können Sie es mit minimalem Locking machen.

Nicht Verwenden Sie einen kritischen Abschnitt ( lock Anweisung) um die tatsächliche Play Methode / Operation, wie einige Leute vorschlagen, Sie können sehr leicht mit einem lock Konvoi . Sie müssen sperren, aber Sie sollten es nur für sehr kurze Zeit tun, nicht während ein Sound tatsächlich spielt, was eine Ewigkeit in der Computerzeit ist.

In etwa so:

%Vor%

Damit können Sie die Anzahl der gespielten Sounds drosseln, indem Sie maxSize auf einen vernünftigen Wert wie 5 setzen. Danach werden einfach neue Anfragen verworfen. Der Grund, warum ich ein Thread anstelle von ThreadPool verwende, besteht einfach darin, einen Verweis auf den verwalteten Thread aufrechtzuerhalten und eine ordnungsgemäße Bereinigung bereitzustellen.

Dies verwendet nur einen Thread und eine Sperre, so dass Sie niemals einen Konvoi haben und niemals Sounds gleichzeitig spielen werden.

Wenn Sie Schwierigkeiten haben, dies zu verstehen, oder mehr Details benötigen, sehen Sie sich Threading in C # und gehen Sie zum Abschnitt "Producer / Consumer Queue".

    
Aaronaught 28.03.2010 19:46
quelle
2

Der einfachste Code, den Sie schreiben könnten, wäre wie folgt:

%Vor%

Obwohl es sehr einfach ist, könnte es ponentiell zu Problemen führen:

  1. Wenn Sie viele (längere) Sounds gleichzeitig spielen, wird es viele Locks geben und viele Threadpool-Threads werden aufgebraucht.
  2. Die Reihenfolge, in der Sie die Sounds eingereiht haben, ist nicht notwendigerweise die Reihenfolge, in der sie wiedergegeben werden.

In der Praxis sollten diese Probleme nur relevant sein, wenn Sie häufig eine Menge Sounds abspielen oder wenn die Sounds sehr lang sind.

    
bitbonk 28.03.2010 19:02
quelle
1

Eine weitere Option, wenn Sie die (wesentliche) vereinfachende Annahme machen können, dass alle Versuche, einen zweiten Sound zu spielen, während der erste noch spielt, nur ignoriert sind, ein einzelnes Event zu verwenden:

%Vor%

Dieser ist ganz einfach, mit dem offensichtlichen Nachteil, dass er einfach "extra" Sounds wegwirft, anstatt sie in die Warteschlange zu stellen. Aber in diesem Fall kann es genau sein, was Sie wollen, und wir können den Thread-Pool verwenden, da diese Funktion sofort zurückkehrt, wenn ein Sound bereits abgespielt wird. Es ist im Grunde "frei von Sperren".

    
Aaronaught 28.03.2010 23:30
quelle
0

Erstelle deinen Thread wie folgt:

%Vor%

Und dies wird dein Thread-Einstiegspunkt sein.

%Vor%     
Steven Evers 28.03.2010 18:55
quelle
0

Die Verwendung von ThreadPool ist nicht der Fehler. Der Fehler liegt in der Warteschlange jedes Sounds als Arbeitselement. Natürlich wird der Thread-Pool mehr Threads starten. Das ist es, was es tun soll.

Erstellen Sie Ihre eigene Warteschlange. Ich habe eine (AsyncActionQueue). Es stellt Elemente in die Warteschlange und wenn es ein Element hat, startet es ein ThreadPool-Arbeitselement - nicht eines pro Element, EINS (es sei denn, eines ist bereits in der Warteschlange und nicht beendet). Der Callback stellt im Grunde Elemente und verarbeitet sie.

Dies ermöglicht es mir, dass X-Warteschlangen Y-Threads teilen (d. h. keine Threads verschwenden) und immer noch sehr nette asynchrone Operationen erhalten. Ich benutze das für eine Comples UI Trading-Anwendung - X Windows (6, 8) Kommunikation mit einem zentralen Service-Cluster (dh eine Reihe von Diensten) und sie alle Async-Warteschlangen verwenden, um Elemente hin und her zu bewegen (gut, vor allem in Richtung der Benutzeroberfläche) ).

Eine Sache, die Sie beachten müssen - und das wurde bereits gesagt - ist, dass wenn Sie Ihre Warteschlange überlasten, wird es zurückfallen. Was dann zu tun ist, hängt von dir ab. Ich habe eine Ping / Pong-Nachricht, die regelmäßig in einen Dienst eingereiht wird (aus dem Fenster) und wenn sie nicht rechtzeitig zurückgegeben wird, wird das Fenster grau und markiert "Ich bin abgestanden", bis es aufholt.

    
TomTom 28.03.2010 19:36
quelle
0

Die neue TPL Dataflow Library von Microsoft könnte eine gute Lösung für solche Dinge sein. Sehen Sie sich das Video hier an - das erste Codebeispiel, das Ihren Anforderungen entspricht, entspricht genau Ihren Anforderungen.

Ссылка

    
Tom Davies 02.10.2012 15:53
quelle