Laden...

Methode in Backgroundworker ausgeführt braucht viermal solange

Erstellt von schirmi87 vor 13 Jahren Letzter Beitrag vor 13 Jahren 5.632 Views
S
schirmi87 Themenstarter:in
13 Beiträge seit 2010
vor 13 Jahren
Methode in Backgroundworker ausgeführt braucht viermal solange

Hallo,
ich bin mittlerweile am Verzweifeln mit meinem Backgroundworker!
In meiner Anwendung lese ich Sensorwerte aus und speichere diese in einer Array List! Damit die Oberfläche in dieser Zeit nicht stehen bleibt geschieht dies in einem Backgroundworker. Leider brauche ich für das Sammeln der Werte in meiner Schleife fast 20sec dafür. Wenn ich es nicht als Backgroundworker ausführe sind es nur noch 5sec. Kann mir vielleicht jemand einen Tip geben warum das sooooooo.... lange dauert?
Vielen Dank für eure Hilfe!

schirmi87

 public void StartMeasuring()
        {
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerSupportsCancellation = true;
            worker.WorkerReportsProgress = false;

            worker.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                Double measureValue = 0.0;
                for (int i = 0; i < 1000; i++)
                {
                    for (int j = 0; j < 2; j++)
                    {
                        measureValue = Taster.ReadLiveValue(j);
                        measureValues[j].Add(measureValue);
                    }
                }
            };

            worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
            {
              MessageBox.show("Fertig");
            }
            };
            worker.RunWorkerAsync(); 
}

So brauche ich nur** 5sec** wenn ich es direkt ausführe, aber die Oberfläche in dieser Zeit eingefroren bleibt.

 
            Double measureValue = 0.0;
            for (int i = 0; i < PruefPlan.MeasuringPoints; i++)
            {
                for (int j = 0; j < probeCont.Count; j++)
                {
                    measureValue = Taster.ReadLiveValue(j);
                    measureValues[j].Add(measureValue);
                }
            }
            MessageBox.Show("Fertig");
B
387 Beiträge seit 2005
vor 13 Jahren

Hallo schirmi87,

komisch.. was genau passiert denn hinter dem Taster.ReadLiveValue bzw. ist das eine Methode von dir?

Gruß
Roland

Gelöschter Account
vor 13 Jahren

Alles was ich dir sagen kann ist das ich das Problem mit dem Backgroundworker gut kenne. Bei mir werden dann aus 45 sekunden schnell mal 3 Minuten.
Warum weshalb wieso kann ich leider auch nur vermuten, evtl. die Thread Prio. bzw. das der eigentliche Gui Thread zuviel Aufmerksameit auf sich zieht - keine Ahnung.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo schirmi87,

... in einer Array List!

ArrayList gehört in die Mottenkiste und sollte wie alle untypisierten Collections aus System.Collections nicht mehr benutzt werden. Verwende stattdessen List<T> und alle anderen typisierten Collections aus System.Collections.Generic.

herbivore

B
387 Beiträge seit 2005
vor 13 Jahren

Hallo Kollegen,

also das Backgroundworker langsamer ist sollte m. E. nicht sein. Ist mir bis jetzt so auch noch nie aufgefallen. Folgender kleiner Test-Code gibt bei mir gleiche Ergebnisse (mit und ohne Backgroundworker):

BackgroundWorker m_bgWorker;

/// <summary>
/// Testing using Backgroundworker.
/// </summary>
private void OnCmdTest1Click(object sender, RoutedEventArgs e)
{
    long time = 0;
    m_bgWorker = new BackgroundWorker();
    m_bgWorker.DoWork += (x, y) => { time = DoSomething(); };
    m_bgWorker.RunWorkerCompleted += (x, y) => MessageBox.Show("Finished in " + time + " milliseconds!");
    m_bgWorker.RunWorkerAsync();
}

/// <summary>
/// Testing without Backgroundworker.
/// </summary>
private void OnCmdTest2Click(object sender, RoutedEventArgs e)
{
    long time = DoSomething();
    MessageBox.Show("Finished in " + time + " milliseconds!");
}

/// <summary>
/// Simulates some work.. ;)
/// </summary>
public long DoSomething()
{
    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();

    //Simulation some work here
    for (int loop = 0; loop < Int32.MaxValue; loop++) { }

    stopWatch.Stop();
    return stopWatch.ElapsedMilliseconds;
}

Beide Test brauchen ungefähr 5,5 Sekunden.

Gruß
Roland

Gelöschter Account
vor 13 Jahren

Hallo Blacal,

Ich vermute weil du nicht auf Gui Elemente zugreifst.
ich schätze nämlich die Verzögerungen kommen daher.

B
387 Beiträge seit 2005
vor 13 Jahren

Hallo Sebastian,

genau von sowas gehe ich aus, denn deswegen habe ich oben gefragt, was genau da innerhalb von DoWork gemacht wird. Wenn jedemenge Invokes aufgerufen werden ist es klar, dass der Backgroundworker länger braucht. In diesem Fall kann man sich auf so wenige Invokes wie möglich beschränken, um die Performance zu steigern. Eventuell kann man auch BeginInvoke verwenden, da hier der Worker-Thread nicht wartet, bis die Gui aktualisiert ist, allerdings muss man aufpassen, dass man die Gui nicht zuballert.

Gruß
Roland

F
10.010 Beiträge seit 2004
vor 13 Jahren

[FAQ] Warum blockiert mein GUI? Abschnitt "Die Falle".

Gelöschter Account
vor 13 Jahren

Also ich habe es jetzt nochmal ohne extra Gui Ausgabe getestet.

WorkerReportsProgress = false;

Der Effekt bleibt der gleiche, der Backgroundworker braucht bis zu 10x so lange.
Meine Hoffnung das es im ReleaseBuild nicht auftritt hat sich leider auch zerschlagen. Irgendeine Idee woran das liegen könnte?

F
10.010 Beiträge seit 2004
vor 13 Jahren

Du machst doch irgendwo selber invokes, und diese "sind die falle".

Wie sieht denn dein code aus?

Gelöschter Account
vor 13 Jahren

Hallo FZelle,
Nein keine Invokes, ich hatte wie gesagt testweise sämtlichen Gui Code entfernt.
Es sieht so als ob z.B. Befehle die das Filesystem betreffen(laden von Dateien)
deutlich langsamer sind, ich arbeite noch dran das zu genauer zu prüfen.
Der Code ist recht umfangreich und hier schlecht zu posten.
Falls du dir das wirklich antun willst.
http://latebindingapi.codeplex.com/SourceControl/list/changesets
Einfach den letzten Stand downloaden.
Wenn ich zb. die Excel TypeLibrary ohne Use Backgroundworker analysiere
dauert das auf meinem Rechner 1:23 mit Backgroundworker dann schon mal
10 Minuten.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Sebastian.Lange,

... ich arbeite noch dran das zu genauer zu prüfen.

das geht am besten mit einem Profiler, aber das weiß du vermutlich. Ich wollte es nur zur Sicherheit schreiben.

herbivore

F
10.010 Beiträge seit 2004
vor 13 Jahren

@Sebastian.Lange:
Da man das Projekt nicht compilieren kann, kann man auch nicht sehen wo da die Zeit verplempert wird.

D
91 Beiträge seit 2005
vor 13 Jahren

Hallo herbivore!

...
das geht am besten mit einem Profiler, aber das weiß du vermutlich. Ich wollte es nur zur Sicherheit schreiben.

Kannst du einen Profiler empfehlen?

Viele Grüße,
Florian

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo deerhunter,

Kannst du einen Profiler empfehlen?

es gibt mehrere Threads im Forum, in denen Profiler empfohlen wurden. Bitte benutze die Forensuche.

herbivore

Gelöschter Account
vor 13 Jahren

Hallo FZelle
Also ich habs gerade getestet und kanns prima kompilieren.
Was ist dennn deine Fehlermeldung? Ich vermute mal .NET 3.5 fehlt
oder aber wahrscheinlicher die TlbInf32.dll ActiveX Dll von MS(Api zum auslesen von TypeLibraryInfos) fehlt auf dem System.

F
10.010 Beiträge seit 2004
vor 13 Jahren

TLI fehlt.

Aber da hast du auch schon dein Problem, COM/ActiveX und Multithreading passen nicht zusammen.

Gelöschter Account
vor 13 Jahren

Ja den leisen Verdacht hatte ich auch schon.
Der macht für die Komponente nochmal einen Thread auf und
es kommt es dann zu Wartezeiten so wie ich das jetzt getrackt habe.
Ob es bei schirmi87 so ähnlich ist wäre noch ganz interessant zu wissen.

B
387 Beiträge seit 2005
vor 13 Jahren

Hallo Sebastian,

ich bin mir nicht ganz sicher, aber eventuell hat es etwas mit dem thread-apartment zu tun. Der Gui-Thread läuft ja i. d. R. als STAThread. Der Hintergrund-Thread vom BackgroundWorker müsste ein MTAThread sein.

Versuche mal selbst einen Thread anzustarten, der mit STAThread markiert ist und messe da die Zeit.

Gruß
Roland

Gelöschter Account
vor 13 Jahren

Geniale Idee. Das mache ich gleich mal.
Ich werde berichten.

Gelöschter Account
vor 13 Jahren

Leider hat das mit dem STA Tread nicht funktioniert.
Ohne Thread 02:03 und mit STA Thread 13:53 *heavy

Mein (recht schnell zusammengezimmerter) STA Thread Code sieht dabei so aus.


    public delegate void ThreadProgressHandler();
    public delegate void ThreadCompletedEventHandler();

    internal class ThreadJob
    {
        Thread _thread = null;
        private System.Windows.Forms.Timer _endTimer;

        public event ThreadStart DoWork;
        public event ThreadCompletedEventHandler RunWorkerCompleted;

        public bool IsAlive
        {
            get 
            {
                return _thread.IsAlive;
            }  
        }

        public void Start()
        {
            _thread = new Thread(DoWork);         
            _thread.SetApartmentState(ApartmentState.STA);
            _thread.Priority = ThreadPriority.Normal;
            _thread.IsBackground = false;
            _thread.Start();
            _endTimer = new System.Windows.Forms.Timer();
            _endTimer.Interval = 100;
            _endTimer.Tick += new EventHandler(_endTimer_Tick);
            _endTimer.Enabled = true;
        }

        public void Abort()
        {
            _thread.Abort();
        }

        private void _endTimer_Tick(object sender, EventArgs e)
        {
            if (false == _thread.IsAlive)
            {
                if (null != RunWorkerCompleted)
                    RunWorkerCompleted();

                _endTimer.Enabled = false;
            }
        }
    }

B
387 Beiträge seit 2005
vor 13 Jahren

Komische Sache..

wenn ich jetzt einmal laut denke.. Wies aussieht, will dieses Excel-Interop zeugs irgendwas mit dem aktuellen Gui-Thread machen. Theoretisch könnte man mal per Application.AddMessageFilter(...) schauen, ob während der Zeit, in der auf Excel-Interop zugegriffen wird, irgendwelche Nachrichten ankommen. Falls dem so wäre, könnte man einfach in dem Worker-Thread eine neue Applications-Nachrichtenschleife starten, per Application.Run oder sowas.

Gruß
Roland