C# Queue Handler als Task

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von mausekeks.

    C# Queue Handler als Task

    Hallo Community,


    ich hätte mal wieder eine Frage.

    Aktuell habe ich einen QueueHandler als Thread-Methode

    Spoiler anzeigen

    C#-Quellcode

    1. private void Handler()
    2. {
    3. try
    4. {
    5. while (WaitHandle.WaitAny(syncEvents.EventArray) != -1)
    6. {
    7. USBMessage item = null;
    8. bool exec = true;
    9. lock (((ICollection)this.messageQueue).SyncRoot)
    10. {
    11. switch (this.messageQueue.Count > 0)
    12. {
    13. case true:
    14. {
    15. item = this.messageQueue.Dequeue();
    16. }
    17. break;
    18. case false:
    19. {
    20. exec = false;
    21. }
    22. break;
    23. }
    24. if (exec)
    25. {
    26. ProcessUSBMessage(item);
    27. }
    28. }
    29. }
    30. }








    mit folgender Start-Methode:

    Spoiler anzeigen

    C#-Quellcode

    1. private void CreateQueueHandler()
    2. {
    3. this.requestHandler = new Thread(Handler);
    4. this.requestHandler.Name = "QueueHandler";
    5. requestHandler.IsBackground = true;
    6. this.requestHandlerTerminating = false;
    7. this.syncEvents.NewItemEvent.Set();
    8. this.requestHandler.Start();
    9. }




    Macht es sinn diese als Task zu implementieren?

    Wenn ja, welche vorteile ergeben Sich daraus und wie würde man dies am besten realisieren

    Lg Mausekeks
    Brain is Loading: 35%
    Eine Frage die du dir Stellen könntest ist: Wie relevant ist allokationen vs echtzeit(nicht wirklich echtzeit aber trotzdem eine gewisse vorhersehbarkeit, die z.B. bei Spielen benötigt wird) vs start delay?

    Allokations bedingt sind die Concurrent Implementierungen schlecht, da diese eine LinkedList verwenden. D.h. für Dinge, die nicht plötzlich den GC haben sollten nicht zu empfehlen(also nicht für Spiele)...
    Vorteil dieser ist jedoch dass weniger locking stattfindet und eher teils busy waiting(SpinWait z.B.) was den start delay sehr niedrig hält...

    Dein Code oben ist aus branching Sicht auch nicht gerade optimal. switch sollte ebenfalls nicht für bools verwendet werden, wird zwar evtl. vom JITC entsprechend optimiert trotzdem mMn auch schlechter lesbar.

    C#-Quellcode

    1. if (this.messageQueue.Count > 0)
    2. {
    3. ProcessUSBMessage(this.messageQueue.Dequeue());
    4. }

    ist wesentlich lesbarer, du sparst dir einen branch, einen else part und eine variable, und noch ein compare^^
    Eine weiter Überlegung wäre evtl auch dies hier zu machen:

    C#-Quellcode

    1. while (this.messageQueue.Count > 0)
    2. {
    3. ProcessUSBMessage(this.messageQueue.Dequeue());
    4. }

    Abhängig davon wie teuer das verarbeiten einer solchen USB message ist, könnte dies sinnvoll sein, da man wesentlich weniger locking braucht, ggf auf kosten der parallelität(jenach frequenz der eingehenden messages). Eine weitere Idee wäre dann evtl. maximal 4(variabel) auf einmal zu bearbeiten, oder gar in Abhängigkeit der anderen Threads. Die komplexität steigt, aber jenach Ziel kann eben entsprechend optimiert werden.

    Wenn die Reihenfolge der ausgeführten Queue nicht so relevant ist, dann wäre natürlich noch eine Überlegung ob man nicht jedem Handler eine eigene Queue gibt, wobei dann in eine dieser Queues abhängig von der Anzahl der bereits beinhalteten Elemente eingefügt wird(umso weniger Elemente um so eher wird eingefügt). Ein Vorteil davon wäre, dass man weiß, dass immer nur ein Thread probiert zu lesen und man darauf entsprechend optimieren kann und anstelle von locking über atomarische Operationen(Interlocked) arbeiten könnte und eine eigens optimierte Queue dafür schreiben...

    Aber am Ende braucht solch starken Optimierungen sowieso fast niemand, weshalb du mit entweder deiner bereits vorhanden Lösung(jedoch ohne switch -> s.o.) oder einer ConcurrentQueue ganz gut läufst...


    Edit: Tasks sind sowieso noch einmal eine ganz andere Geschichte und werden sowieso über eine Art Threadpool abgearbeitet, was dafür sorgt, dass natürlich das Pooling nicht mehr von dir übernommen werden muss. Task Performance ist dabei jedoch nicht die optimalste, da es dann doch einen gewissen Overhead für die Bequemlichkeit mitgibt. Aber wie bei allem, ist die Frage ob das bisschen Performance für dich relevant ist, oder aber auch wie viel du von der Bequemlichkeit tatsächlich hast und danach wählst du dann das aus, was dir besser gefällt, oder was du brauchst...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Danke für diese Zusammenfassung.

    ich werde bei meiner bestehenden Methode bleiben und diese überarbeiten.
    Habe gestern etwaige andere Lösungsansätze probiert, auch mit Tasks und bin zu dem ergebnis gekommen das es bei mir damit am besten funktioniert.


    Vielen lieben Dank

    Lg Mausekeks
    Brain is Loading: 35%