Autor Beitrag
AXMD
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Di 15.04.08 18:41 
Hallo!

Ich beschäftige mich seit kurzem mit Multithreading in .NET und habe testweise ein Programm geschrieben, das Dateien in einem separaten Thread rekursiv in einem vorgegebenen Ordner sucht und das Ergebnis in einer ListBox ausgibt. Hier der relevante Codeausschnitt des Threads:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
                FilesFoundListBox.Invoke(new VoidCall(FilesFoundListBox.BeginUpdate));
                FilesFoundListBox.Invoke(new VoidCall(FilesFoundListBox.Items.Clear));
                SetText(FilesFoundGroupBox, "Files found");
                UnfilteredFileList = ... //Dateisuche (der Übersicht halber entfernt)
                FilesFoundListBox.Invoke(new ObjectArrayParamCall(FilesFoundListBox.Items.AddRange), new object[] { UnfilteredFileList });
                FilesFoundListBox.Invoke(new VoidCall(FilesFoundListBox.EndUpdate));


wobei ich für die Invoke-Aufrufe noch diese beiden Delegates deklariert habe:

ausblenden C#-Quelltext
1:
2:
        delegate void VoidCall();
        delegate void ObjectArrayParamCall(object[] Param);


Wenn ich jetzt beispielsweise C:\ durchsuchen lasse, dauert das sehr lange, aber ich kann während die Suche läuft das Formular wie gewohnt bedienen. Sobald allerdings FilesFoundListBox.Items.AddRange aufgerufen wird, reagiert das Hauptformular nicht mehr und wird für ca. eine Minute weiß. Da ich Invokes verwende ist das prinzipiell logisch, da der Hauptthread AddRange ausführt.
Nun meine Frage: wie kann ich dennoch dafür sorgen, dass mein Formular während des Hinzufügens der Items in die ListBox bedienbar bleibt? Gibt es eventuell Alternativlösungen?

AXMD
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mi 16.04.08 10:27 
Na dann würde ich einfach die Items einzeln hinzufügen ;) . Dann lässt sich (ohne Begin/EndUpdate) auch das (flackernde) Befüllen beobachten. Irgendwann werden aber die tausend Invoke-Aufrufe selbst natürlich zum Flaschenhals, man könnte also stattdessen z.B. immer 10 Items auf einmal zur ListBox schicken. Oder im Nebenthread eine Queue befüllen und diese dann im Hauptthread mit einem 100ms-Timer abarbeiten... naja, wir wollen mal nicht übertreiben :mrgreen: .

PS: Vielleicht ist hier BeginInvoke (asynchron) besser geeignet, damit wartet der Such-Thread nicht mehr auf die GUI-Aktionen in den Invokes.
AXMD Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Mi 16.04.08 11:27 
user profile iconKhabarakh hat folgendes geschrieben:
Na dann würde ich einfach die Items einzeln hinzufügen ;) .

Das erhöht doch die Dauer zusätzlich

user profile iconKhabarakh hat folgendes geschrieben:
Dann lässt sich (ohne Begin/EndUpdate) auch das (flackernde) Befüllen beobachten.

Das halte ich nicht für sinnvoll, vor allem weil das Flackern störend ist und unnötig viel Zeit kostet

user profile iconKhabarakh hat folgendes geschrieben:
Irgendwann werden aber die tausend Invoke-Aufrufe selbst natürlich zum Flaschenhals, man könnte also stattdessen z.B. immer 10 Items auf einmal zur ListBox schicken.

Das ändert aber auch nichts an der Tatsache, dass das Formular in der Zwischenzeit nicht reagiert

user profile iconKhabarakh hat folgendes geschrieben:
Vielleicht ist hier BeginInvoke (asynchron) besser geeignet, damit wartet der Such-Thread nicht mehr auf die GUI-Aktionen in den Invokes.

Es spielt keine Rolle, ob der Nebenthread wartet oder nicht - es geht darum, dass das Hauptformualar bedienbar bleibt.

AXMD
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Mi 16.04.08 11:40 
user profile iconAXMD hat folgendes geschrieben:
user profile iconKhabarakh hat folgendes geschrieben:
Na dann würde ich einfach die Items einzeln hinzufügen ;) .

Das erhöht doch die Dauer zusätzlich

Wenn Du eine Aktualisierung der GUI willst, braucht das nunmal Rechenzeit. Eine eingefrorene GUI wird immer weniger Rechenzeit brauchen, als eine, die reagiert. Erinnere Dich an Application.ProcessMessages aus den Delphi-Zeiten ;-)

Du schreibst zwar, die GUI hänge bei AddRange, aber es dürfte eher der Aufruf von EndUpdate sein, oder? Denn erst dann wird die GUI aktualisiert. Und zu dem Zeitpunkt wird halt ein großer Batzen an Items auf einmal in die Listbox gepackt und das blockiert. Ich sehe auch nur die Möglichkeit, immer nur ein paar Items in die Listbox zu packen , damit die GUI zwischendurch Zeit hat, sich zu Aktualisieren.

In der WPF hätte man die Möglichkeit, bei Invoke noch eine Priorität mitzugeben, aber das ist mir aus WinForms nicht bekannt.

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
AXMD Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Mi 16.04.08 12:27 
Dann werde ich heute Abend mal versuchen, die Listbox 10-Items-weise zu befüllen. Alternativ schwebt mir eine ListView vor (ist die eventuell schneller?) - habe aber hier in der Arbeit keine Zeit, um das zu testen.

Danke für deine Antwort
AXMD