Laden...

Sehr hohe CPU-Auslastung bei Multi-Threading

Erstellt von DJCMay vor 9 Jahren Letzter Beitrag vor 9 Jahren 4.153 Views
D
DJCMay Themenstarter:in
3 Beiträge seit 2015
vor 9 Jahren
Sehr hohe CPU-Auslastung bei Multi-Threading

Hallo alle zusammen,

ich habe nun schon einiges ausprobiert und hoffe nun, dass ich hier einen neuen Denkanstoß bekommen kann.

Ich habe ein kleines Konsolenprogramm geschrieben, dass in bestimmten Intervallen Daten von verschiedenen Quellen einsammeln soll. Dabei sind die Quellen die gleichen (jeweils eine eigene Splunk-Instanz), daher habe ich entsprechende Klassen geschrieben. Da ich vermeiden wollte, dass sich die Abfragen gegenseitig blockieren, habe ich jede Umgebung als Thread gestartet:


Thread PROD = new Thread(prod_log.update);
Thread OT = new Thread(ot_log.update);
Thread GW = new Thread(gw_log.update);

In der update-Funktion läuft derzeit eine while(true)-Schleife. Die Zeit bis zum nächsten Abruf habe ich schon auf verschiedene Arten versucht zu überbrücken:


ewh.WaitOne(interval * 1000);
Thread.CurrentThread.Join(interval * 1000);
System.Threading.Thread.Sleep(interval * 1000);

Funktionieren tut das alles auch, allerdings verbraucht das Programm 50% CPU, als 2 volle Cores. Das ist natürlich nicht akzeptabel. Habt ihr eine Idee, wie ich das optimieren kann? Andere Variante zum Warten? Ganz weg von Threads? Bin für jede Idee dankbar!

Gruß
Christoph

16.834 Beiträge seit 2008
vor 9 Jahren

while(true) macht man heute nicht mehr; das erzeugt nur unnötigen CPU verbrauch.
Schau Dir Tasks an; hier besonders TPL Pipelines und den Consumer Producer Pattern über die sogenannte BlockingCollection.

PS: Du kannst Dir auch [Artikel] Multi-Threaded Programmierung durchlesen, was sehr ratsam ist.
Dort hat man das mit Hilfe von Monitor gelöst; aber im Verhältnis zu modernen Tasks trotzdem (ver)alte(te) Schule.

189 Beiträge seit 2014
vor 9 Jahren

... und wenn es dir nur um den Zeitzyklus gehen sollte, solltest du überlegen, ob evtl. nicht ein Timer cleverer wäre.

W
872 Beiträge seit 2005
vor 9 Jahren

Thread.Sleep() selber erzeugt keine Last (außer dem Overhead für den Context Switch). Wahrscheinlich hast Du irgendwo anders ein Problem oder an einer Stelle vergessen das Sleep einzubauen und while (true) läuft ohne Sleep.

P
157 Beiträge seit 2014
vor 9 Jahren

Vielleicht hilft dir das weiter:

      

  [Test]
        public void ThreadTest()
        {
            ResetEvent = new ManualResetEvent(false);
            Task.Run((Action)MyTask);

            //dient nur zum keep alive des unittests
            Thread.Sleep(15000);
            Stop();
        }

        private ManualResetEvent ResetEvent;

        private void MyTask()
        {
            
           while (WaitHandle.WaitAll(new WaitHandle[] { ResetEvent }, 1000))
           {
               Trace.WriteLine(string.Format("Zeit :{0}", DateTime.UtcNow));
           }

        }
        public void Stop()
        {
            ResetEvent.Set();
        }

Thread.Sleep() sollte man eigentlich gar nicht verwenden...

vg

Wenn's zum weinen nicht reicht, lach drüber!

D
DJCMay Themenstarter:in
3 Beiträge seit 2015
vor 9 Jahren

Erstmal vielen Dank für die vielen und schnellen Antworten.

Ich habe jetzt versucht, mich an dem Beispiel von Parso zu orientieren und habe in den Konstruktoren Folgendes eingebaut:


ResetEvent = new ManualResetEvent(false);
Task.Run((Action)update);

Die Funktion update()


do
{

    // do something

} while (WaitHandle.WaitAll(new WaitHandle[] {ResetEvent}, (interval * 1000)));

Jetzt ereilt mich das Problem, dass dies zwar einmalig ausgeführt wird, aber die Wiederholungen finden nicht statt. Wo ist mein Denkfehler?

Gruß
Christoph

16.834 Beiträge seit 2008
vor 9 Jahren

Ich versteh das Beispiel von Parso nicht, denn der Beschreibung zufolge ist es ein ziemlich Glas-klarer Fall vom Consumer-Producer-Pattern. 🤔

D
DJCMay Themenstarter:in
3 Beiträge seit 2015
vor 9 Jahren

Also wenn ich das Prinzip von Consumer-Producer-Pattern richtig verstanden habe, dann geht es um erzeugende und abholende Threads, richtig?

Dies ist bei mir eigentlich nicht der Fall. Die Threads holen sich die Daten einerseits von Splunk-Servern und aus einer MySQL-Datenbank. Da ich aber nicht möchte, dass die einzelnen Umgebungen sich gegenseitig blockieren, falls mal eine Verbindung zu einem der Server ausfällt, habe ich sie in Threads gepackt.

Aber weismat schon angemerkt hat, scheint das Problem doch noch wo anders zu liegen. Zu mindestens konnte ich jetzt die CPU-Last auf 25% reduzieren, nachdem ich einen Fehler eliminiert habe. Ich würde aber trotzdem gern auf Tasks umstellen, wenn das möglich ist.

Gruß
Christoph

16.834 Beiträge seit 2008
vor 9 Jahren

Du hast Task#1, der von Splunk#1 in Deinen Bag#1 pusht.
Du hast Task#2, der von Splunk#2 in Deinen Bag#1 pusht.
Du hast Task#N, der von Splunk#N in Deinen Bag#1 pusht.

Du hast Task#M, der Element#X aus Bag#1 holt und verarbeitet.
Das Ergebnis gibt er aus, macht sonst irgendwas oder pusht es in Bag#2 für eine weitere Pipeline-Verarbeitung.

So verstehe ich Deine Beschreibung.

Gelöschter Account
vor 9 Jahren

while(true) macht man heute nicht mehr; das erzeugt nur unnötigen CPU verbrauch.

Da hat unser Abt schon mal recht. Eine while Schleife ist eine while Schleife ist eine while Schleife. Ob die jetzt was tut oder nicht ist völlig egal. Die versucht aus dem System immer das maximale rauszuholen. Der Versuch das mit Sleep im Body der Schleife auszugleichen ist meiner Sicht nur dann hilfreich wenn der Sleep Intervall im mehrstelligen Sekundenbereich liegt. Wenn das System kürzerer Reaktionszeiten garantieren soll dann ist Sleep niemals das Mittel der Wahl.

In der update-Funktion läuft derzeit eine while(true)-Schleife. Die Zeit bis zum nächsten Abruf habe ich schon auf verschiedene Arten versucht zu überbrücken

Warum??? Dafür bieten sich delegates an. Gib einen Methodenzeiger rein und lass dich zurück rufen. (Evtl musst du dann hier synchronisieren was sich im einfachsten Fall durch ein lock Statement realiseren lassen würde)