Laden...

WPF Deadlock beim Anzeigen/Aktualisieren von Daten im zweiten Fenster - Wie kann man das verhindern?

Erstellt von weismat vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.597 Views
W
weismat Themenstarter:in
872 Beiträge seit 2005
vor 8 Jahren
WPF Deadlock beim Anzeigen/Aktualisieren von Daten im zweiten Fenster - Wie kann man das verhindern?

Ich habe in meiner WPF App selten Deadlocks und versuche zu verstehen, wie ich das verhindern kann.
In Window 1 habe ich eine ObservableCollection, die durch einen Socket-Thread aktualisiert wird und die als Source für ein DataGrid in dem Window gebunden ist.
Dazu benutze ich zur Zeit folgenden Code.


Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate
                    {
                        Items.Insert(0, new Item(socketData));
                        if (Items.Count > maxCount)
                            Items.RemoveAt(maxCount-1);
                    }));

Das Deadlock passiert nun, wenn der User auf ein Item im dem Grid einen Click macht, so daß in Window 2 die angezeigten Daten angepasst werden. Da sieht der Code bisher so aus:

 
private void Grid_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
   try
            {
                var grid = sender as DataGrid;
                if (grid.CurrentItem != null)
                {
                    var item = grid.CurrentItem as Item;
                    if (item.CanFindData())
                    {
                        window2.Update(item.Key(), item.Text);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("ex:" + ex);
            }
}

Was muss ich machen um den Deadlock zu verhindern. Ich hatte mal beide Methoden mit einem lock versehen, aber das hat nicht geholfen. Muss ich das den Click Update im Window2 auch auf dem Dispatcher aufrufen? Da ich schon auf dem GUI Thread bin, dachte ich nicht, daß ich das brauchte.

742 Beiträge seit 2005
vor 8 Jahren

An welcher Stelle ist der Deadlock genau?

W
weismat Themenstarter:in
872 Beiträge seit 2005
vor 8 Jahren

Der Deadlock ist im WPF selber.
Im Buch von Huber zu WPF steht zum Scheduler drin, dass man Application.Current.Dispatcher.BeginInvoke nehmen soll, um den Deadlock zu vermeiden, aber das reicht nicht aus...
Ich habe im 2. Window auch eine gebundene Observable Collection, wo der Verweis geändert wird. Update: Die habe ich nun auch in den gleichen Scheduler gepackt - leider macht das wohl keinen Unterschied .

5.657 Beiträge seit 2006
vor 8 Jahren

Hi weismat,

wenn du stattdessen MVVM und DataBinding verwendest, mußt du dich nicht mehr um diese Details kümmern und solche Probleme treten gar nicht erst auf.

Christian

Weeks of programming can save you hours of planning

W
weismat Themenstarter:in
872 Beiträge seit 2005
vor 8 Jahren

Diesen pauschalen Kommentar kann ich nicht verstehen.
Die Daten sind ja gebunden. Wie soll ich denn sonst den Click auf das Grid abarbeiten? Ich kann es mittels Command machen, aber ich denke nicht, daß das mein Problem löst.
Das Grundproblem ist doch die Synchronisation zwischen einer externen Quelle, die das Grid verändert und Clicks auf das Grid selber.

5.657 Beiträge seit 2006
vor 8 Jahren

In deinem Szenario würdest du die Controls im Detailfenster an das CurrentItem des Grids im Hauptfenster binden. Wenn du Daten aus mehreren Threads in eine Liste speichern willst, benötigst du allerdings trotz allem im ViewModel eine Thread-sichere Auflistung. BeginInvoke ist nur für die Aktualisierung von UI-Elementen gedacht, nicht jedoch, um Collections Thread-sicher zu machen.

Christian

Weeks of programming can save you hours of planning

W
weismat Themenstarter:in
872 Beiträge seit 2005
vor 8 Jahren

Was meinst Du mit Thread-Sichere Auflistung?
Dafür benutze ich zurzeit die ObservableCollection - würdest Du an der Stelle etwas anderes empfehlen?
Muss ich beim Ändern des Verweises der Bindung den Scheduler benutzen oder nicht?

5.657 Beiträge seit 2006
vor 8 Jahren

Was meinst Du mit Thread-Sichere Auflistung?

Soetwas zum Beispiel: Concurrent Observable Collection, Dictionary, and Sorted Dictionary. Hier gibt es auch noch ein paar nützliche Links: How to make ObservableCollection thread-safe?

Muss ich beim Ändern des Verweises der Bindung den Scheduler benutzen oder nicht?

Was meinst du mit Ändern des Verweises? Wenn sich die Daten ändern, werden die UI-Elemente durch DataBinding automatisch aktualisiert. Oder willst du das Binding-Ziel während der Laufzeit ändern? (Und wenn ja: Warum?)

Christian

Weeks of programming can save you hours of planning

3.003 Beiträge seit 2006
vor 8 Jahren

Was meinst Du mit Thread-Sichere Auflistung?
Dafür benutze ich zurzeit die ObservableCollection

Thread Safety
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

ObservableCollection ist nicht thread-safe. Alternativen hat MrSparkle genannt.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

742 Beiträge seit 2005
vor 8 Jahren

Eine Threadsichere Collection ist doch überhaupt nicht notwendig, wenn Weismat wie hier gezeigt, alles über den UI Thread läuft. Da muss noch was anderes schief laufen.

5.657 Beiträge seit 2006
vor 8 Jahren

Hi malignate,

ich beziehe mich auf:

eine ObservableCollection, die durch einen Socket-Thread aktualisiert wird

Außerdem ist die Fehlermeldung relativ eindeutig.

Christian

Weeks of programming can save you hours of planning

742 Beiträge seit 2005
vor 8 Jahren

Eine Zeile weiter steht doch, dass er den Dispatcher dazu benutzt. Ist das die einzige Stelle, weismat?

Bist du sicher, dass es ein Deadlock ist? Also die Anwendung hängt einfach? Auch ohne Debugger?

W
weismat Themenstarter:in
872 Beiträge seit 2005
vor 8 Jahren

Das es ein Deadlock im WPF selbst ist, ist ziemlich sicher. Sowohl mit und ohne Debugger nachvollziehbar und ich sehe bestimmte Worker weiterlaufen, ergo hängt der GUI Thread - sogar so, daß nicht mehr Beenden funktioniert und der Task Manager zum Stoppen nötig ist.

In Window1 arbeite ich mit dem Dispatcher - das Race ist zwischen Window1 Click im Grid, der das Grid in Window 2 neu erstellt und Window 1 Update vom Grid. Hatte gehofft, daß der PseudoCode dazu klar war.

Mein Grundproblem ist mehr, daß die ObservableCollection nicht eine Add Methode für eine Enumaration hat und einzelnes Add zu langsam ist.

Ich habe noch eine andere Alternative gefunden:


BindingOperations.EnableCollectionSynchronization(Lines, Locker);

Ich werde mich aber ohnehin mit Euren Vorschlägen beschäftigen.
Da ich schon in anderen Teilen meiner App Rx benutze, werde ich mir auch Dynamic Data anschauen.

5.657 Beiträge seit 2006
vor 8 Jahren

Ganz allgemein würde ich evtl. noch empfehlen, die Funktionalität etwas besser zu kapseln. In der Präsentationsschicht sollte man eigentlich überhaupt keine Datenquellen abfragen. Der Empfang der Daten vom Socket sollte in der Datenzugriffsschicht stattfinden. Dort sollten dann Funktionalitäten zur Abfrage der Daten von außen (entweder per Polling oder per Event-Benachrichtigung) zur Verfügung gestellt werden.

Die Logikschicht fragt dann die Daten ab und bereitet sie für die Anzeige und/oder Bearbeitung auf.

In der Präsentationsschicht solltest du dich nur um die Anzeige der empfangenen Daten kümmern, dafür sollte dann eine normale ObservableCollection ausreichen, die an das Grid gebunden ist.

Das hat auch den Vorteil, daß du jede Funktionalität unabhängig von den anderen testen kannst.

Christian

Weeks of programming can save you hours of planning