2016-07-21 19 views
1

Ich entwickle Windows Form C# -Programm, die Excel-Daten von freigegebenen Laufwerk liest alle 20 Minuten (ich verwende "Timer") - Funktion "Einfügen". Ich möchte mehrere Excel-Dateien gleichzeitig wegen der Leistung lesen. Aus diesem Grund verwende ich Threads.Ausführen von mehreren Threads

Jeder Thread ruft eine Funktion (LoadExcelData) auf, die Daten von Excel in ArrayList liest. Ich möchte wissen, wann alle Threads beendet sind (wenn alle Excel-Dateien in ArrayList geladen wurden), um diese ArrayList in die interne Datenbank einzufügen.

Ich versuchte mit Thread [i] .Join(), aber das friert GUI ein. Ich weiß auch nicht, was passieren würde, wenn ich über 100 Dateien habe und aus diesem Grund 100+ Threads. Würde das eine Speicherausnahme oder eine andere Ausnahme verursachen?

 //Execute every 20 minutes (Timer). Do not Execute in case previouse run is not finished 
     void inserting(List<String> excels){ 

     int numOfThreads=excels.length; 
     Thread[] threads = new Thread[numOfThreads]; 
     for (int index = 0; index < numOfThreads; index++) 
     { 
      int i = index; 
      threads[index] = new Thread(() => 
       { 
        LoadExcelData(excels[i].File_name); //function loads excel data to global array "Weather" which is used later on 
       }); 
     } 

     for (int i = 0; i < threads.Length; i++) 
     { 
      threads[i].Start(); //start thread 
     } 

     for (int i = 0; i < threads.Length; i++) 
     { 
      // threads[i].Join(); //this freezes GUI! 
     } 

     InsertToDB(object of ArrayList<ClassName>); //insert data which was read from Excels 

     isRunning=false;//Data was successefully inserted to DB 
    } 

Ich möchte dies alle 20 Minuten ausführen. Ich benutze Timer:

timer = new System.Windows.Forms.Timer(); 
    timer.Tick += new EventHandler(timerEventHanlder); 
    timer.Interval = 20 * 60000; // in miliseconds 
    timer.Start(); 

private void timerEventHanlder(object sender, EventArgs e) 
{ 
    List<String> excels = getExcels(); 
    if (!isRunning){ //in case previous timer even is not finished wait another 20 minutes... 
     isRunning=true; //flag to true 
     inserting(excels); 
     } 
} 

Gibt es bessere Wartezeiten, um das obige Problem zu lösen?

+1

Persönlich verwende ich kaum jemals Threads aus Gründen der Leistung. Ich benutze sie fast immer, um die Benutzeroberfläche ansprechbar zu halten. Eigentlich denke ich Threads _reduce_ die Leistung, aufgrund der Cross-Thread-Kommunikation overhand und Sperren/Synchronisieren. –

+1

Von Anfang an habe ich das für die gleiche Sache gemacht - GUI war eiskalt. Aber dann musste ich wissen, wann alle Threads wegen des Timers beendet sind und ich habe das thread.Join() Event benutzt, welches die GUI wieder einfrieren ließ. Leistung verbessert in meinem Fall, weil zwei (oder mehr) Excel-Datei schneller verarbeitet werden. – michael24B

Antwort

1

Der UI-Thread wird eingefroren, weil Sie eine System.Windows.Forms.Timer verwenden, die das tickergesteuerte Timer-Ereignis auf dem UI-Thread auslöst; Dies ist nützlich, weil Sie keine Invoke irgendetwas auf dem Tick-Ereignis haben. Der Aufruf von Join blockiert den aufrufenden Thread und in Ihrem Fall ist dies der UI-Thread.

Um dies zu vermeiden (und da Sie nicht zu Invoke keine UI-Elemente benötigen), können Sie Ihre System.Windows.Forms.Timer zu einem System.Timers.Timer, die aus dem UI-Thread trennen in einem Thread läuft ändern. Wenn Sie zu einem System.Timers.Timer wechseln, müssen Sie einige der Syntax in Ihrem Code ändern (z. B. das Tick-Ereignis ist das Elapsed-Ereignis stattdessen usw.).

dort ist auch die System.Thread.Timer und die System.Web.UI.Timer, zusätzlich könnte man auch einen zweiten Thread Laichen aus dem Timer-Tick-Ereignisse innerhalb des UI-Thread auf dem Gewinde zu vermeiden warten, Beispiel:

private void timerEventHanlder(object sender, EventArgs e) 
{ 
    (new System.Threading.Thread(() => { 
     List<String> excels = getExcels(); 
     if (!isRunning){ //in case previous timer even is not finished wait another 20 minutes... 
      isRunning=true; //flag to true 
      inserting(excels); 
     } 
    })).Start(); 
} 

einen Start new thread ändert den aktuellen Code nicht und ermöglicht es Ihnen, ihn zu ändern, wenn Sie jemals etwas in der Benutzeroberfläche aufrufen müssen.

Sie beantworten sind andere Frage aber:

ich auch nicht wissen, was passieren würde, wenn ich mehr als 100 Dateien haben und aus diesem Grund mehr als 100 Threads. Würde das eine Speicherausnahme oder eine andere Ausnahme verursachen?

Laichen 100+ Threads werden keine Ausnahmen verursachen, wenn Ihr Code eine spezifische Ausnahme hat (wie ein Null-Delegierten als ThreadStart bestanden), oder wenn das Betriebssystem nicht einen Thread erstellen, die, wenn das Betriebssystem kann Wenn du einen Thread erstellst, hast du größere Probleme. Es ist möglich, dass Speichererschöpfung auftreten kann, da das Thread ein verwaltetes Objekt ist und daher Speicher belegt (zusammen mit einem ArrayList, aber die Speichermenge für 100+ Threads (sogar 1000+) ist auf jedem System, das ausgeführt werden kann, vernachlässigbar Das .NET-Framework (selbst auf den meisten Embedded-Systemen), so dass die Anzahl der Threads nicht unbedingt ein Problem sein wird.

Mit Blick auf Ihren Code, möchten Sie möglicherweise anstelle von 100+ Threads, die Verwendung der System.Threading.ThreadPool und ein System.Threading.CountDownEvent, Beispiel:

CountdownEvent Countdown; 

void LoadExcelData(object data) 
{ 
    // loads excel data to global array "Weather" which is used later on 
    Countdown.Signal(); 
} 

//Execute every 20 minutes (Timer). Do not Execute in case previouse run is not finished 
void inserting(List<object> excels) 
{ 
    Countdown = new CountdownEvent(excels.Count); 
    int i = 0; 
    while (i < excels.Count) { 
     ThreadPool.QueueUserWorkItem(LoadExcelData, excels[i++].File_name); 
    } 
    Countdown.Wait(); 

    InsertToDB(WeatherList); //insert data which was read from Excels 
    isRunning = false; //Data was successefully inserted to DB 
} 

Dies wird das System-Thread-Pool nutzen, um Ihre Funktionen auszuführen und ermöglicht .NET handl e die Planung der Threads, um massive Ressourcenkonflikte zu vermeiden, wenn die Anzahl der Threads sehr hoch ist. Sie können andere Methoden verwenden, um zu blockieren, wie eine Mutex oder Semaphore, aber die CountDownEvent kapselt ziemlich viel, was Sie mit anderen warten-Objekten tun müssten und die Threads aus dem Thread-Pool beitreten.

Um ehrlich zu sein, da Sie Daten aus Excel-Dateien in mehreren Threads lesen, wenn nicht jeder Thread den gesamten Inhalt der Datei in RAM liest und die Operationen auf diese Weise ausführt, sehen Sie möglicherweise keine enorme Leistungssteigerung . Multi-Threaded-Anwendungen mit schwerem E/A sehen normalerweise keine große Leistungssteigerung, es sei denn, die E/A befindet sich auf leistungsorientierten Geräten oder die erste Eingabe der gesamten Datei wird in den Arbeitsspeicher gelesen. Nur eine Randnotiz, da Sie mit Dateien multi-threading sind.

Es sollte auch beachtet werden, dass die Verwendung der System.Threading.ThreadPool ideal für Threads ist, die Sie erwarten, nur für ein paar Sekunden oder so zu laufen; Wenn Sie erwarten, dass ein Thread länger dauern kann, sollten Sie bei der Erstellung der Threads bleiben, wie Sie es jetzt tun. Sie können immer noch die CountDownEvent verwenden und Sie brauchen kein Array von Threads wie Sie haben (Sie könnten einfach die (new Thread(function)).Start() Syntax verwenden).

Hoffe, dass kann helfen

+0

Vielen Dank für Ihre Erklärung, es ist sehr hilfreich. Ich wusste nicht über den Unterschied zwischen System.Timers.Timer und System.Windows.Forms.Timer. Es funktioniert soweit! – michael24B

+1

@ michael24B, froh, dass ich helfen konnte! Ich habe meine Antwort mit einigen weiteren Informationen aktualisiert, da ich festgestellt habe, dass Sie eine Frage zum Erstellen mehrerer Threads hatten. – txtechhelp

+0

Danke für Ihre zusätzliche Erklärung.Ist es möglich, mehr Funktionen mit ThreadPool.QueueUserWorkItem aufzurufen? Ich möchte LoadExcelData und xy Funktion (en) in demselben Thread aufrufen. – michael24B

1

Der übergeordnete Thread wird die for-Schleife erreichen, die alle Worker-Threads verbindet und dort wartet, bis alle Threads beendet sind (und verbunden werden können). Wenn die GUI in demselben übergeordneten Thread ausgeführt wird, kehrt die Ausführung nicht zur GUI zurück, bis alle Threads beendet sind. Dies wird sehr lange dauern, da Sie Timer eingerichtet haben. Versuchen Sie, die GUI in einem anderen Thread auszuführen.

Edit: Auch eine Randnotiz, würde ich Ihre Timer-Längen auf etwas viel kürzer, während Sie Debuggen, um zu sehen, ob es tatsächlich wartet, wie Sie es erwarten. Sobald Sie es richtig funktioniert haben, können Sie es wieder auf 20 Minuten einstellen.

+0

Irgendwelche Ideen, wie ich GUI-Thread von Elternteil trennen und Funktionalitäten unberührt lassen sollte? Danke - Ich verwende bereits ein kürzeres Intervall (2 Minuten) für Debugging-Zwecke. – michael24B

+0

Ohne Ihren Code zu sehen, nein. Ich würde versuchen, alles, was mit der GUI zu tun hat, in eine eigene Methode, Klasse oder was auch immer zu ziehen, so dass Sie einen Thread starten und ihn abschicken können, um GUI-Arbeit zu leisten, wenn Ihr Programm gestartet wird. Allerdings habe ich noch nicht wirklich mit ihnen gearbeitet, daher bin ich mir nicht sicher, ob es Standardarchitekturen für GUIs gibt, denen Sie folgen sollten, besonders wenn Sie Multithreading einführen. –

Verwandte Themen