Backgroundworker zu schnell



  • Hallo, ich möchte einer datagridview Komponente Zeilen zuweisen, die über einen backgroundworker Thread gefüllt werden. Wenn ich den Thread ungebremst laufen lasse, werden immer nur die zuletzt berechneten Werte eingetragen. Lasse ich den Thread jedoch 25 msec schlafen, wird alles korrekt gemacht. Ich würde das Ganze jetzt gerne so synchronisieren, dass ich mir die Sleep Funktion sparen kann. Ich habe versucht den Schreibvorgang in das gridview mit Hilfe von Monitor, lock, und ReaderWriterlock abzuschotten, aber das hat nicht funktioniert. Weiß da jemand abhilfe? Es ist halt nur 1 thread, der aber zu schnell rechnet.
    Vielen Dank schon mal



  • Zeig bitte relevanten Code



  • Also, gerne verwendet man WaitHandles (AutoResetEvent oder ManualResetEvent) zur Threadsynchronisation.
    So zum Beispiel:

    class Program
        {
            static AutoResetEvent waithandle;
    
            static void Main(string[] args)
            {
                waithandle = new AutoResetEvent(false);
    
                ThreadPool.QueueUserWorkItem( (o) =>
                {
                    waithandle.WaitOne();
                    Console.WriteLine("done");
                });
    
                Thread.Sleep(3000);
                waithandle.Set();
    
                Console.ReadKey(true);
            }
        }
    

    Was mir aber seltsam vorkommt, ist Dein Versuch, einen Thread zum Befüllen eines GUI-Controls zu verwenden. Nur der GUI-Thread darf auf Controls zugreifen! (Abgesehen von InvokeRequired, Invoke,...)

    Deshalb ... zeig Deinen Code 😉



  • Hallo,

    danke schon mal für die Reaktion. Der relevante code ist folgender:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                string[] erg = new string[3];
                int anz = Convert.ToInt32(e.Argument);
    
                for (int i = 0; i < anz; i++)
                {
                    //System.Threading.Thread.Sleep(25);
                    erg[0] = tmp.getSubID();
                    erg[1] = tmp.getHostRange();
                    erg[2] = tmp.getSubBroadcast();
                    backgroundWorker1.ReportProgress(i, erg);
                    tmp.IP = tmp.getNextSubIP(netbits, subbits, i + 1);
                }
            }
    
            private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                    string[] erg = (string[])e.UserState;
                    this.dataGridView1.Rows.Add(e.ProgressPercentage, erg[0], erg[1], erg[2]);   
            }
    

    Also in der Do_Work Funktion werden die entsprechenden Werte belegt und mit ReportProgress dann an den GUI Thread weitergegeben.
    Ich hoffe das ist erst mal ausreichend. Wie gesagt: mit der Zeitverzögerung klappt das alles wunderbar...



  • Mach mal string[] erg = new string[3]; in die for-Schleife. (Arrays sind Referenztypen. Klingelts?)

    Btw. Deine Berechnungen sind so schnell (oder?), dass der Einsatz einen Workers mehr als fraglich ist.



  • Hallo µ,
    das hat schon mal einiges an Geschwindigkeit gebracht. Dass Arrays Verweistypen sind, weiß ich schon, aber was macht jetzt genau den Unterschied aus wo ich das hinschreibe. Klingt, als ob ich ein absoluter newbi wäre, aber ich komme von C++ her und hatte da ganz andere Probleme. Ein zweiter Punkt ist der: Ich wollte das Befüllen in einen zweiten Thread auslagern, da bis zu 8838608 Zeilen eingetragen werden müssen und ich nicht wollte, dass die ganze Anwendung "einfriert". Aber genau das passiert jetzt. Liegt das am worker oder hab ich etwas falsch gemacht. Wie ließe sich das denn vermeiden? wäre da ein "echter Thread" besser geeignet?
    Vielen Dank schon mal



  • Ebi01 schrieb:

    Hallo µ,
    das hat schon mal einiges an Geschwindigkeit gebracht. Dass Arrays Verweistypen sind, weiß ich schon, aber was macht jetzt genau den Unterschied aus wo ich das hinschreibe.

    Aber es funktioniert, wenn Du das string[] in die Schleife packst, ja?
    Erklärung wäre, dass der Thread einfach weiter iteriert und die träge GUI in ProgressChanged (imho steckt da ein BeginInvoke dahinter -> asynchroner Wechsel in den GUI-Thread) beim Zugriff auf das Array auf Elemente zugreift, die schon längst überschrieben wurden. Deshalb siehst Du nur die letzten Einträge (mehrfach wahrscheinlich).
    Das ist jedenfalls exakt das Verhalten, dass ich mit einem kleinen Testprogramm beobachtet habe.

    Ebi01 schrieb:

    Klingt, als ob ich ein absoluter newbi wäre, aber ich komme von C++ her und hatte da ganz andere Probleme.

    Quatsch. C# ist einfach anders. Hat nichts mit Newbiesein zu tun. Bin übrigens den gleichen Weg gegangen 😉

    Ebi01 schrieb:

    Ein zweiter Punkt ist der: Ich wollte das Befüllen in einen zweiten Thread auslagern, da bis zu 8838608 Zeilen eingetragen werden müssen und ich nicht wollte, dass die ganze Anwendung "einfriert". Aber genau das passiert jetzt. Liegt das am worker oder hab ich etwas falsch gemacht. Wie ließe sich das denn vermeiden? wäre da ein "echter Thread" besser geeignet?
    Vielen Dank schon mal

    8 Mio. Einträge? 😮

    Der Worker ruft einfach viel zu oft ProgressChanged auf, was jeweils den GUI-Thread belastet. Da der BW deutlich schneller ist als die lahme GUI, friert alles ein. (Der Worker ist übrigens ein echter Thread)

    Du könntest das Lesen der Einträge in größere Portionen einteilen. Vielleicht 100. Oder 1000. Oder mehr. In einem Thread oder BWorker. Diese Portionen werden dann auf einen Schlag in das Control geschrieben und danach die nächsten Einträge gelesen. Das verursacht weniger Wechsel in den GUI-Thread, belastet ihn dann aber trotzdem. Wenn das der Flaschenhals ist, wird es keine Lösung sein.
    Ein Timer könnte auch helfen. Einfach den Timer portionsweise Daten in das Control schreiben lassen und zwischen den Callbacks des Timers restliche Events von der GUI verarbeiten lassen, damit nichts einfriert. Es gab kürzlich eine Lösung für ein ähnliches Problem hier im Forum.

    Aber:
    1. Kein Monitor kann 8 Mio Einträge anzeigen und kein Benutzer kann mit einer solchen Menge etwas anfangen. Denke mal ernsthaft darüber nach, die Daten "On-Demand" zu laden. Oder besser zu strukturieren und nur teilweise anzuzeigen.

    2. Solche Mengen habe ich noch nicht in ein DataGridView geschrieben. Bricht es dabei von der Performance her nicht vollkommen ein? Das DGV hat einen Virtual-Mode (http://msdn.microsoft.com/de-de/library/system.windows.forms.datagridview.virtualmode(v=vs.80).aspx). Das entspräche der "On-Demand" Lösung



  • Ich hab halt einen Subnetzrechner nachprogrammiert, der das auch mit den 8 Millionen Zeilen hinbekommt. (und zwar innerhalb einer Sekunde). Würde es evtl. etwas bringen, wenn ich ein datagridview im Hintergrund fülle und es erst dann wenn ich fertig bin dem Grid auf dem Formular zuweise?



  • Wenn Hintergrund == anderer Thread: Nicht möglich.

    Im GUI-Thread entspräche es dem Timer-Vorschlag von oben. Dann spielt es keine Rolle ob das Control angezeigt wird oder nicht.

    Willst Du 8 Mio. IP-Adressen eines Subnetzes anzeigen? Wozu?

    Der Virtual-Mode scheint die sinnvollste Lösung zu sein.


Anmelden zum Antworten