none
Zugriff auf Application.Current.MainWindow bleibt hängen RRS feed

  • Frage

  • Hallo,

    wir haben eine multithreaded UI Umgebung.

    Zur Reduktion der Komplexität seien nur 2 Threads betrachtet.

    Thread 1 (T1), der main thread der Application.
    Thread 2 (T2), ein anderer UI Thread.

    T2 erzeugt das MainWindow und setzt es in

    Application.Current.MainWindow auf folgende Weise

    trace info vorher

    Application.Current.Dispatcher.Invoke( () => Application.Current.MainWindow = myMainWindow )

    trace info danach

    Der Code läuft in sehr vielen Fällen astrein durch.
    Manchmal bleibt der Code aber einfach hängen.
    Im Trace file ist das tracing davor zu sehen, aber nicht das tracing danach.
    Exception wird auch keine geworfen, da unser globaler Exception Handler nicht angesprochen wird.

    Die Frage ist:
    Warum funktioniert das manchmal nicht?

    Bitte keine Workarounds und Fragen warum machst du das so und so.

    Wir wollen das Problem verstehen und keine Alternativvorschläge diskutieren.

    Wer kann hier helfen?

    Vielen Dank


    • Bearbeitet _Red_Baron_ Donnerstag, 28. Januar 2016 13:05
    Donnerstag, 28. Januar 2016 13:03

Antworten

  • Hallo,

    wir konnten das Problem nun analysieren und verstehen.
    Auf der Maschine auf der die Tests laufen treten aus diversen Gründen Ressource-Probleme auf (Memory-Consumption, Processor-Load, Disk space consumption, etc. ).

    Bei unseren Langlauf-Tests, kommt es deshalb sporadisch zu Laufzeit-Problemen.

    Konkret ist es so, dass der in den oben genannten Fällen T2 genannte Thread seinen Invoke call absetzt, während der T1 den Dispatcher Run noch nicht erfolgreich erreicht hat. Vermutlich ist T1 ausgebremst durch Ressourcen Probleme.

    Dadurch hängt T2.

    In unserer reduzierten Tests ist das nie auf getreten, da die Systemlast dabei vermutlich zu gering war.

    Vielen Grüße

    RB



    Mittwoch, 9. März 2016 10:52

Alle Antworten

  • Hi,
    problematisch ist, wenn der UI Thread (Dein T2) nicht der Main-Thread ist. Auch ist aus dem Codeschnipsel nicht ersichtlich, in welchem Thread die Instanz von "myMainWindow" erzeugt wird. Wenn diese Instanz in Deinem UI Thread (T2) erzeugt, dann sind mit Sicherheit Probleme zu erwarten, da Du die Zuweisung nicht im UI Thread, sondern im Main Thread machst.

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Donnerstag, 28. Januar 2016 21:42
  • Hallo und vielen Dank für die Antwort.

    Diese Antwort bringt jedoch keinerlei Erkenntnis.
    Die Frage war nicht, ob es zu Problemen kommt sondern warum es diese eine Problem gibt.

    Es wurde ebenso erwähnt, dass das main window in T2 erzeugt wird.

    Es ist noch zu ergänzen, dass die Applikation in den Dauertests ohne Probleme läuft, wenn wir das
    Code-Schnipsel  rausnehmen und ein eigenes Property für das MainWindow halten und aus den diversen Threads drauf zugreifen. Deswegen brauchen wir auch keine Workarounds. Wir wollen das konkrete Problem verstehen. Hinweise, dass es zu Problemen führen kann sind nicht zielführend und wenn man so will eine Tautologie zu dem Ur-Posting.


    Diese gezeigt Stelle bleibt hängen. Also noch mal die Frage:
    Warum bleibt der Code hängen?

    Vielen Dank und viele Grüße


    • Bearbeitet _Red_Baron_ Freitag, 29. Januar 2016 08:05
    Freitag, 29. Januar 2016 07:52
  • Hi Roter Baron,
    für viele Programmierer ist Threading schwarze Magie und wird oft nicht verstanden. Deine Antwort scheint ein Beispiel solcher Falscheinschätzung zu sein. Du scheinst nicht verstanden zu haben, dass es Objekte gibt, die bei threadübergreifenden Zugriffen zufällige und nur schwer reproduzierbare fehlerhafte Zustände erzeugen können.

    Beim Multi-Threading steuert das Betriebs das Zuweisen der CPU-Ressourcen auf dem Niveau der Maschinenbefehle. Eine Methode oder Eigenschaft eines Objektes besteht im Standardfall aus vielen Maschinenbefehlen. Wenn beispielsweise eine Methode bzw. Eigenschaft aufgerufen wird, werden üblicherweise Inhalte aus dem RAM zur Verarbeitung in CPU-Register geladen und verändert. Vor dem Abspeichern der bearbeiteten Werte kann beim Multithreading diese Folge der Maschinenbefehle an beliebiger Stelle unterbrochen werden und die Registerinhalte werden im Stack gepuffert. Ein anderer Thread greift dann auch auf die Inhalte im RAM zu, verändert sie und speichert die veränderten Werte ab. Der ursprünglich unterbrochene Thread wird fortgesetzt und speichert seine zwischenzeitlich gepufferten geänderten Werte, was zu Inkonsistenzen mit nicht kalkulierbaren Zuständen führen kann. Ein analoges Szenario tritt ein, wenn beide Threads in unterschiedlichen Kernen gleichzeitig mit den gleichen Speicherplätzen arbeiten. Die inkonsistenten Zustände können von vielen Faktoren abhängen, wie Anzahl der CPU-Kerne, parallele Belastungen, zufällige Serialisierung von Zugriffen auf andere Ressourcen usw. Zusätzlich können auch Racing Conditions von Bedeutung sein.

    Aus den genannten Problemen, versucht die Laufzeitumgebung den threadübergreifenden Zugriff bei nicht threadsicheren Objekten zu erkennen und als Fehler anzuzeigen. Das funktioniert aber nicht in allen Fällen, obwohl seit dem Framework 1 schon vieles verbessert wurde.

    Unter diesen Gesichtspunkten sollte der main-Thread als UI Thread genutzt werden, in dem alle bezüglich der Oberfläche nicht threadsicheren Zugriffe konzentriert werden.

    In Deinen Ausführen hast Du zwar etwas von der Instanziierung eines MainWindow in einem separaten Thread (T2) erwähnt, ob myMainWindow aber der Verweis auf diese Instanz ist, ist aus deinen Ausführungen unklar geblieben. Wenn myMainWindow der Verweis auf eine in T2 erzeugte Instanz eines UI-Objektes ist, dann darf mit dieser Instanz nicht im Main Thread gearbeitet werden, wie Du in Deinem Codeschnipsel gezeigt hast. Das kann manchmal zufällig funktionieren, eine stabile Arbeitsweise ist so aber nicht möglich, wie Du schon selbst bemerkt hast.

    Ich hoffe, dass der kurze Exkurs zum Multithreading zu einem besseren Verständnis der fehlerhaften Zustände durch diese Programmierfehler geführt haben.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Freitag, 29. Januar 2016 12:02
  • Hallo Peter,

    vielen Dank für Deine Ausführung. Wir benötigen keine Erklärung wie man mit mehreren Threads arbeitet und was im Hintergrund passiert. Ich würde mich nie trauen jemanden zu sagen, dass er scheinbar etwas nicht verstanden hat, ohne den Entwicklerhintergrund der Person zu kennen. Wir wissen wie threads  zu synchnonisieren und UI Objekte in den diversen Threads zu handeln sind.
    Unser Code arbeitet auch korrekt. Nur wenn wird das Application MainWindow Objekt vom .Net Framework nutzen hängt genau dieser call sporadisch. Nutzen wird das Application MainWindow Objekt nicht und legen die Instanz statt im .Net Property, in unserem eigenen Property ab läuft alles sauber, ohne hänger, ohne deadlocks, ohne das Objekte aus dem falschen Thread Context referenziert werden.


    Die Applikations ist ein Plug-in und läuft per-Design nicht im MainUi Thread und dafür gibt es gute Gründe. Diese sind aber für die Fragestellung nicht relevant.

    Eine "sollte" Aussage bedeuted "muss nicht". WPF gibt multithreaded UIs per Design her.
    Wir wissen sehr genau, wie man die Objekt zu verwenden hat und benötigen wie bereits erwähnt keine Hinweis wie man etwas tun soll oder tun kann, sondern eine Antwort warum der konkrete Call hängen bleibt.

    Ein Deadlock scheidet aus, da der MainUI Thread in dem andere UI Komponenten laufen reaktiv bleibt und seine message queue sauber bearbeitet.

    Vielen Dank und viele Grüße



    • Bearbeitet _Red_Baron_ Freitag, 29. Januar 2016 14:28
    Freitag, 29. Januar 2016 13:29
  • Hallo,

    die Aussage das man mit der Main Window Instanz die nicht im Main UI Thread erzeugt wurde nicht arbeiten kann ich so nicht stehen lassen. Der Main UI Thread muss den Dispatcher des Window Objektes verwenden,
    dann kann er auch das MainWindow verwenden.

    Man muss den korrekten Dispatcher verwenden, dann kann jedes UI Objekt aus beliebigen Threads verwendet werden.

    Vielen Dank und viele Grüße



    Freitag, 29. Januar 2016 13:39
  • Hallo,

    es gibt bei uns die Vermutung, dass das Problem im .Net Framework steckt, da dieses evtl. implizit davon ausgeht, dass die in der Applikations-Instanz hinterlegte MainWindow Instanz zwangsweise im Main UI Thread erzeugt werden muss. Es könnte dann zu Hängern kommen, wenn das .Net Framework z.B. bei System Konfigurationsänderungen oder ähnlichen auf die Instanz zugreift. Diese Spekulation hat eine gewisse Berechtigung.

    Kann dies jemand bestätigen oder widerlegen (.Net Code - MSDN Doku, ...)?

    In der MSDN Dokumentation zur Applikationsklasse wird dies nicht explizit gefordert, bzw. explizit verboten.

    Vielen Dank und viele Grüße


    • Bearbeitet _Red_Baron_ Freitag, 29. Januar 2016 14:29
    Freitag, 29. Januar 2016 14:26
  • Hi Roter Baron,
    Du schreibst, dass Dir die Details zum Multithreading bekannt sind, schreibst aber nichts dazu, wie Du den UI Thread erzeugt hast. Was ist mit dem SynchronizationContext, der vor der Windows-Instanziierung zu erzeugen ist? Was ist mit dem Dispatcher, der zu starten ist? Bei fehlerhafter Berücksichtigung dieser wichtigen Details kann es zu Problemen kommen.

    Vielleicht wird das Fenster erzeugt bevor der SynchronizationContext erzeugt und installiert wird. In diesem Fall ist der aktuelle SynchronizationContext null und, wenn unter diesen Bedingungen ein Fenster erzeugt wird, kann es zu unvorhersehbarem Verhalten führen.

    Unklar ist nach allen Deinen Aussagen immer noch, warum Du den Verweis auf die Instanz des Fensters dem Application.Current.MainWindow zuweist, welches zum Main-Thread gehört, das Fenster-Objekt aber zum T2-Thread gehört. Wenn Du schon einen separaten UI Thread haben willst, dann mache das konsequent, ohne dass der Main Thread damit zu tun hat.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Samstag, 30. Januar 2016 01:42
  • Hi Roter Baron,
    Du schreibst, dass Dir die Details zum Multithreading bekannt sind, schreibst aber nichts dazu, wie Du den UI Thread erzeugt hast. Was ist mit dem SynchronizationContext, der vor der Windows-Instanziierung zu erzeugen ist? Was ist mit dem Dispatcher, der zu starten ist? Bei fehlerhafter Berücksichtigung dieser wichtigen Details kann es zu Problemen kommen.

    Vielleicht wird das Fenster erzeugt bevor der SynchronizationContext erzeugt und installiert wird. In diesem Fall ist der aktuelle SynchronizationContext null und, wenn unter diesen Bedingungen ein Fenster erzeugt wird, kann es zu unvorhersehbarem Verhalten führen.

    Unklar ist nach allen Deinen Aussagen immer noch, warum Du den Verweis auf die Instanz des Fensters dem Application.Current.MainWindow zuweist, welches zum Main-Thread gehört, das Fenster-Objekt aber zum T2-Thread gehört. Wenn Du schon einen separaten UI Thread haben willst, dann mache das konsequent, ohne dass der Main Thread damit zu tun hat.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Hallo und vielen Dank für die Antwort.

    Zu:
    Wenn Du schon einen separaten UI Thread haben willst, dann mache das konsequent, ohne dass der Main Thread damit zu tun hat.

    Wie sollen denn die diversen Threads calls in andere Threads rufen, wenn die Objekte die zu den jeweiligen Thread Kontexten gehören von einander nichts Wissen? Die Aussage macht für mich im Ansatz keinen Sinn.

    Es wird mit Dispatcher.Invoke gearbeitet. Da wird kein SynchContext benötigt. Wie .Net innerhalb der Invoke Methode mit einem SynchContext arbeitet erklärt die MSDN leider nicht. Ich muss davon ausgehen, dass wenn der Dispatcher zur Verfügung steht, das .net Framework auch seine internen Synch Objekte bereit hat. Andernfalls müsste in der MSDN erklärt werden ab wann der Dispatcher verwendet werden darf.

    Das MainWindow wird in der Applikation Instanz zur Verfügung gestellt, damit die anderen Threads wiederum
    über diese Instanz per Dispatcher des MainWindows ihre calls in T2 routen können.

    Wir haben bis vor kurzen das Application MainWindow Property gar nicht verwendet. Stattdessen gab es ein eigenes Property auf dass Objekte aus anderen Threads zugreifen konnten. Bis dahin lief alles, was dieses Thema betrifft stabil.

    Erst nachdem wird die Application MainWindow Instanz von .Net verwenden bleibt genau dieser eine Invoke Call hängen. Wozu soll für diesen konkreten Invoke Call ein expliziter SynchContext benötigt werden?

    Viele Grüße

    Samstag, 30. Januar 2016 07:18
  • Hallo,

    mit einer Aussage wie "es kann zu Problemen kommen" kann leider niemand etwas anfangen.

    Welche konkreten Problem sind gemeint und warum kommt es zu dem Problem.

    Bitte explizit benennen und darstellen welche Evidenzen es dafür gibt.

    Vielen Dank und viele Grüße

    Samstag, 30. Januar 2016 07:23
  • Hi Roter Baron,
    leider kann ich Dir nicht mit Details dienen, warum ein DispatcherObject, von dem ein Window erbt, im Konstruktor einen gültigen SynchronzationContext benötigt. Aus eigener Erfahrung weiß ich nur, dass ein fehlender SynchonizationContext, ein nicht ordnungsgemäß gestarteter Dispatcher und der Zugriff von außerhalb des zugehörenden Dispatchers Probleme bringt. Wenn Dich die genaueren Details interessieren, solltest Du Dich mit dem internen Code des Konstruktors des Dispatcherobjects beschäftigen. Mir reichte bisher meine Erfahrung, dass ein vom DispatcherObject geerbtes Objekt streng nur mit dem mit ihm verbundenen Dispatcher zu arbeiten hat, der natürlich auch richtig gestartet sein muss.

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Samstag, 30. Januar 2016 10:47
  • Hi Roter Baron,

    Wie sollen denn die diversen Threads calls in andere Threads rufen, wenn die Objekte die zu den jeweiligen Thread Kontexten gehören von einander nichts Wissen? Die Aussage macht für mich im Ansatz keinen Sinn.

    Für den objektübergreifenden Zugriff gibt es verschiedene Techniken, wie beispielsweise statische Member. Da muss man nicht Member nutzen, die mit einem anderen Thread verbunden sind und nicht threadsischer sind.

    Es wird mit Dispatcher.Invoke gearbeitet. Da wird kein SynchContext benötigt.

    Dass ein SynchonizationContext für die Datenübermittlung zwischen Threads benötigt wird, sollte eigentlich Basiswissen sein, wenn mit Threads gearbeitet wird. Vereinfacht dargestellt wird der SynchonizationContext benötigt, damit die Laufzeitumgebung den richtigen Punkt ermitteln kann, an welchem der threadübergreifende Zugriff in den Kontext des anderen Threads einzuordnen ist.

    Ich empfehle Dir, mal eine eigene Klasse zu erstellen, die aus separaten threads threadübergreifende Zugriffe realisiert, ohne den vom System dem thread zugeordneten Dispatcher zu nutzen. Das ist sehr interessant und kostet viel Scheiß, bis es stabil läuft.

    Andernfalls müsste in der MSDN erklärt werden ab wann der Dispatcher verwendet werden darf.

    Zum DispatcherObject, von welchem eine Window erbt, steht dazu eigentlich aus meiner Sicht ausreichend viel.

    Das MainWindow wird in der Applikation Instanz zur Verfügung gestellt, damit die anderen Threads wiederum über diese Instanz per Dispatcher des MainWindows ihre calls in T2 routen können.

    Diese Aussage solltest Du nochmals genau beschreiben. Um auf ein Window Objekt zuzugreifen, benötigt man den Dispatcher, der mit dem Thread verbunden ist, in welchem das Window Objekt erzeugt wurde. Die Nutzung des Dispatchers des Main Threads, um auf DispatcherObjects zuzugreifen, die in anderen Threads (bei Dir T2) erzeugt wurden, widerspricht den in der MSDN zum DispatcherObject beschriebenen Grundlagen.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Samstag, 30. Januar 2016 11:15
  • Hallo,

    Zu:

    Die Nutzung des Dispatchers des Main Threads, um auf DispatcherObjects zuzugreifen, die in anderen Threads (bei Dir T2) erzeugt wurden, widerspricht den in der MSDN zum DispatcherObject beschriebenen Grundlagen.

    Das ist Grundlegend falsch.
    Die Dispatcher sind genau  dazu da um von einem Thread in den anderen zu routen widerspricht ganz und gar nicht der Dokumentation.
    Dazu gibt es gefühlte hunderte von Beispielen, unter anderem
    https://msdn.microsoft.com/de-de/library/ms741870%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

    Ich habe nicht gefragt wozu SychContext sind.
    Die Aussage dass dies Basiswissen sei ist korrekt, aber doch ziemlich vermessen.

    Die Frage war,warum konkret bei dem Aufruf des Invokes ein SynchContext benötigt wird.

    Die Frage war auch nicht wie man anderen Objekten einen Zugriff auf Thread übergreifend reentrant zur Verfügung stellt. Ich habe mehrfach erwähnt, dass es mit unseren eigenen Properties funktioniert, nur nicht mir dem Application MainWindow Property, und dieses Objekt stammt aus dem Main thread auf dessen Erzeugung überhaupt kein Einfluß genommen werden kann. Ob das propterty dabei static ist oder nicht ist völlig irrelecant. Wir greifen mittels Dispatcher.Invoke auf das Objekt zu, was auch in völliger Übereinstimmung mit der Doku der MSDN ist.


    • Bearbeitet _Red_Baron_ Samstag, 30. Januar 2016 12:22
    Samstag, 30. Januar 2016 12:09
  • Hi Roter Baron,
    irgendwie drehen wir uns im Kreis und mir scheint, dass es zu viele Verständnisprobleme gibt.

    Um von einem Thread auf einen anderen Thread mittels Dispatcher zuzugreifen, ist immer der Dispatcher des Ziel-Threads zu nutzen, nicht der Dispatcher der Ausgangs-Threads. Das bedeutet, dass man nicht den Dispatcher des Main-Threads für den Zugriff auf Deinen T2 nutzen kann. Voraussetzung dazu ist aber auch, dass der Dispatcher des Ziel-Threads richtig gestartet wurde, incl. SynchronizationContext.

    Das Application-Objekt wird vom Main-Thread erzeugt und für den Zugriff darauf ist auch der Dispatcher des Main-Threads zuständig. Diesen Dispatcher für den Zugriff auf DispatcherObjects zu nutzen, die in einem anderen Thread erzeugt wurden, ist unzulässig. Wenn ich also UIElements in einem eigenen Thread erzeuge, muss ich auch sicherstellen, dass ich den dafür vorgesehenen Dispatcher nutze. Und das kann in diesem Fall nicht der Dispatcher des Main-Threads sein.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Samstag, 30. Januar 2016 12:57
  • Das ist völlig korrekt und das ist genau das was der Code macht.

    Die "Nutzung" des Objektes beschränkt sich dabei auf die Zuweisung des Properties

    und das ist völlig valide.

    Hier noch mal der Code:

    Application.Current.Dispatcher.Invoke( () => Application.Current.MainWindow = myMainWindow )

    Es wird exakt der Dispatcher des Main threads verwendet um das Property zu setzen das nur im main thread gesetzt werden darf.

    Und das ist völlig konform mit der Doku.

    Was danach mit dem MainWindow Objekt im main thread geschieht ist eine ganz andere Frage und hat mit dem Call oben zunächst nichts zu tun.

    Ich hatte allerdings schon erwähnt, dass die Objekte aus den anderen Thread den Dispatcher des MainWindow Objekts verwenden um in T2 wieder zurückzukommen.

    Und auch das ist völlig valide, allerdings steht das in keinen Zusammenhang zum hängenbleiben des Invoke calls.



    Samstag, 30. Januar 2016 13:10
  • Hi Roter Baron,
    und wieder drehen wir uns im Kreis.

    Die Zuweisung zur Eigenschaft MainWindow im Kontext des Main-Threads war aus meiner Sicht unstrittig.

    Dann geht es jedoch los. Lass Dir mal den Inhalt der MainWindow-Eigenschaft im Main-Thread anzeigen. Da kommt ein Fehler wegen Zugriff auf ein DispatcherObject eines anderen Threads. Aus einem anderen Thread auf die MainWindow-Eigenschaft zuzugreifen bringt auch Fehler, weil ApplicationCurrent zum Main-Thread gehört.

    Was soll also das Ganze?


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Samstag, 30. Januar 2016 13:20
  • Hallo,

    Beispiel:
    der main thread T1 erzeugt ein window mit einem button und einem button clickhandler.
    ausserdem

    einen zweiten UI Thread T2 mit einem 2. Fenster in dieser Art:

                Thread thread = new Thread(() =>
                {
                    Window1 w = new Window1();
                    w.Show();

                   // erster invoke

                    Application.Current.Dispatcher.Invoke(() => Application.Current.MainWindow = w);

                    System.Windows.Threading.Dispatcher.Run();
                });

                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();

    Danach folgt app.run() auf dem main thread.

    Der Button-Click-Handler für des erste Window aus T1 läuft per Definition auf T1.
    Der Code sieht wie folgt aus:

            private void button1_Click(object sender, RoutedEventArgs e)
            {
                Window1 w = Application.Current.MainWindow as Window1;

                Application.Current.MainWindow.Dispatcher.Invoke(
                    () =>
                    {
                        w.Title = "Title";
                    });
            }

    Wie zu erwarten kann auf die MainWindow-Instanz zugegriffen werden, der Dispatcher geholt,

    und der Fenster Titel des 2. Fensters, welches in T2 läuft gesetzt werden.

    Ich habe immer gesagt, dass die Objekte aus den anderen Threads immer über den Dispatcher des MainWindows zugreifen.

    Das hat aber alles nichts damit zu tun warum der erste Invoke call sporadisch hängen bleibt.

    Die Aussage,
    "Aus einem anderen Thread auf die MainWindow-Eigenschaft zuzugreifen bringt auch Fehler, weil ApplicationCurrent zum Main-Thread gehört."
    ist nicht korrekt.
    Man muss den entsprechenden Dispatcher verwenden und nichts anderes habe ich immer gesagt.

    Die Frage nach dem was soll das, ist denke ich damit beantwortet.

    Im Gegenzug kann ich fragen was das alles soll mit den Hinweisen auf SynchContext Objekten
    die bei den Dispatchern überhaupt nicht benötigt werden, das die Implementierung nicht der Doku in der MSDN entspricht, die Hinweise, dass man es anders machen kann und die Empfehlung erstmal die Grundlagen zu lesen.





    • Bearbeitet _Red_Baron_ Samstag, 30. Januar 2016 17:19
    Samstag, 30. Januar 2016 16:52
  • Hi Roter Baron,
    Bemerkung zum letzten Beitrag: es fehlt die Zuweisung des SynchronizationContext im T2;


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Samstag, 30. Januar 2016 19:35
  • Hallo,

    das widerspricht meines Erachtens völlig dem Konzept der Dispatcher Lösung mit den .net message pump threads

    und macht keinen Sinn.

    Bei kohärenter Technologie der threads, wie z.B. .net thread/.net threads wird kein SynchContext benötigt.
    Diese Aufgabe über nimmt die message pump und der Dispatcher.

    Die SynchContext Objekt sind entwickelt worden, um nicht-kohärente Technologien zu synchronisieren, wie z.B. WPF zu WinForm zu COM Objekten zu Enterprise Services, etc..

    Was ist also die konkrete und plausible Erklärung, dass ein fehlender SynchContext zu einem Hänger des Invokes von T2 nach T1 sporadisch passieren kann?

    Vielen Dank

    Sonntag, 31. Januar 2016 10:31
  • Hi Roter Baron,
    ich kann die Frage nicht beantworten, warum bei der Instanziierung eines DispatcherObjects der DispatcherSynchronizationContext von Bedeutung ist und warum es bei dessen Fehlen (null) zu fehlerhaften Zuständen kommen kann. Da müsste man tiefer in die Konstruktoren des DispatcherObjects einsteigen.

    Für mich ist trotzdem die Nutzung des zum Main Thread gehörenden Application.Current für die Verwaltung von Dispatcherobjects eines anderen Threads fragwürdig anstelle der Nutzung einer threadsicheren Verwaltung der Verweise.

    Ergänzung:

    Das Problem kann man simulieren, wenn man im Konstruktor von Window1 einen BackgroundWorker startet und Window1 in einem anderen Thread instanziiert nach der Methode, die Du gezeigt hast (ohne SynchronizationContext) und die früher mal vom Microsoft empfohlen und zwischenzeitlich wieder zurückzogen wurde:

                Thread thread = new Thread(() =>
                 {
                     Window1 w = new Window1();
                     w.Show();
                    System.Windows.Threading.Dispatcher.Run();
                 });
                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();
    


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut


    Sonntag, 31. Januar 2016 10:59
  • Hallo,

    wenn man nicht weiß wozu das Objekt benötigt wird, dann sollte man es auch nicht empfehlen, bzw. sogar sagen, dass es fehlt. Das wirkt nach reiner Spekulation, auch noch mit der Information, dass MS das mittlerweile zurückgezogen haben soll. Was soll den jemand mit der Information jetzt anfange?

    Die gezeigte Simulation benötigt selbstverständlich einen SynchContext, da der Backgrundworker keine message pump hat die von einem Dispatcher gehandelt werden kann. Mein Problem besteht aber aus einen message pump .net UI thread bei denen damit per Definition kein synchcontext benötigt wird.

    Das gezeigte Code ist kein Produktcode, sondern eine Reduktion auf das wesentliche Problem.
    Der Code entspricht der Doku und Empfehlungen der MSDN und sonstiger Literatur die mir bekannt ist

    Ein Hinweise dass der Code fragwürdig ist, reicht nicht.
    Warum ist er fragwürdig - konrete Hinweise - sonst ist das ganze nur aufblasen des "Threads" hier ohne jegliche Netto Information, wie bisher.

    Es werden keine SynchContext Ojekte benötigt. Dazu sind die Dispatcher da.
    Falls doch, dann bitte zeigen warum und der Verweis darauf wo es dokumentiert ist.

    Vielen Dank.

    Sonntag, 31. Januar 2016 11:44
  • Hallo,

    der SynchContext wird beim Aufruf von Invoke implzit angelegt wenn keiner da ist.
    Nachzusehen bei

    http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs,4990431309c71f1a

    Der Invoke call holt sich den Current SynchContext. Dieses Property liefert wiederum einen neuen zurück, wenn keiner da ist.

    Das bestätigt letztendlich die MSDN Doku zu message pump basierten .net threads und der Interthread-Kommunikation mittels Dispatcher. 

    Die Dispatcher, wie auch die SynchContext Objekte haben auch nicht mit einer thread-sicheren Verwendung von Objekten zu tun sondern regeln nur wie Code in einem anderen Thread ausgeführt wird.

    Die Objekte müssen darüber hinaus noch thread sicher genutzt werden mit entsprechenden Mechanismen wie
    lock, semaphores, mutexen, critical sections.

    Das ist aber ein ganz anderes Thema .

    Viele Grüße



     

    Sonntag, 31. Januar 2016 13:34
  • Hi Roter Baron,
    zu Deine letzten Ausführungen ist nur zu ergänzen, dass mit dem Dispatcher.Run ein SynchronizationContext erzeugt wird, wenn er noch nicht vorhanden war.

    Das Problem aber ist, das zum Zeitpunkt der Instanziierung des Window1 noch kein SynchronizationContext vorliegt. Deshalb wäre es interessant zu wissen, was im Konstruktor eines DispatcherObjects in diesem Fall passieren kann. Dazu kann ich aber nichts sagen.

    Für Deine Test kann ich nur wiederholt empfehlen, das mal zu ändern und zu schauen, ob dann das Problem immer noch vorliegt.

    Nichtsdestotrotz bleibe ich bei meiner vorher geäußerten Meinung, dass ich die Nutzung des zum Main Thread gehörenden Application.Current für die Verwaltung von Dispatcherobjects eines anderen Threads fragwürdig finde anstelle der Nutzung einer threadsicheren Verwaltung der Verweise. Ich weiß nicht, was die Laufzeitumgebung mit solchen Verweisen macht und eine ggf. erforderliche doppelte Dispatcherisierung nicht ausführt. Du hast ja selbst geschrieben, dass es bei Deiner vorherigen Verwaltung der Verweise nicht im Application.Current keine Probleme gab.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Sonntag, 31. Januar 2016 14:39
  • Hallo,

    was im Konstruktor des Dispatcher Objektes passiert ist einsehbar bei:
    http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs,078d6b27d9837a35

    Weiterhin wird der Zugriff auf dem internen Dispatcher Mechanismus mit z.B. lock(_globalObject) abgesichert.

    Was bei der Instanziierung von Window1 passiert ist auch wenig interessant, da dass Objekt zu dem Zeitpunkt niemandem bekannt ist.

    Erst aber der Zuweisung ins Application MainWindow Property steht die Referenz anderen Objekt zur Verfügung.

    Danach wird jeder call in T2 über den Dispatcher in T1 geroutet, notwendige SynchContext Objekte werden bei Bedarf erzeugt. Ein expliziter Context wird nicht benötig. 
    Die calls entsprechen der MSDN Doku. Ich kenne keine MSDN Seite in der dieser Mechanismus anders beschrieben wird.

    Du findest es fragwürdig, was die MSDN  beschreibt, aber hast keine konkreten Anhaltspunkte für die Fragwürdigkeit.

    Etwas auszuprobieren ob es auf einen anderen Weg funktioniert hat nicht geklärt warum es nicht funktioniert.
    Das hat zur Konsequenz, in Zukunft wegen diesem Unverständnis in die selbe Falle laufen.

    Mittlerweile habe ich trotz alle dem einen SynchContext erstellt und die Test laufen seit einigen Stunden.
    Das Problem ist wieder 2 mal aufgetreten.

    Ich benötige also keine Vermutungen, oder Workaround oder ähnliches.
    Bitte konkrete plausible Darstellung von möglichen Ursachen.

    Alles andere wie allgemeine Hinweise das etwas passieren kann, aber ich weiß nicht warum sind lediglich zeitraubend.

    Vielen Dank.

    Sonntag, 31. Januar 2016 15:30
  • Hallo,

    wir konnten das Problem nun analysieren und verstehen.
    Auf der Maschine auf der die Tests laufen treten aus diversen Gründen Ressource-Probleme auf (Memory-Consumption, Processor-Load, Disk space consumption, etc. ).

    Bei unseren Langlauf-Tests, kommt es deshalb sporadisch zu Laufzeit-Problemen.

    Konkret ist es so, dass der in den oben genannten Fällen T2 genannte Thread seinen Invoke call absetzt, während der T1 den Dispatcher Run noch nicht erfolgreich erreicht hat. Vermutlich ist T1 ausgebremst durch Ressourcen Probleme.

    Dadurch hängt T2.

    In unserer reduzierten Tests ist das nie auf getreten, da die Systemlast dabei vermutlich zu gering war.

    Vielen Grüße

    RB



    Mittwoch, 9. März 2016 10:52
  • Hi,
    vielen Dank für die Rückmeldung und die Bestätigung, dass die Ursache in unzureichender Verrieglung lag.

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    Mittwoch, 9. März 2016 13:16
  • Hallo,

    das ist keine unzureichende Verriegelung.
    Der T1 wurde verdrängt und konnte den Dispatcher nicht starten.
    Was soll hier verriegelt werden und wie?

    T1 müsste T2 mitteilen, dass der Dispatcher jetzt bereit wäre.
    Genau das kann er aber nur tun, wenn T1 den Dispatcher run noch nicht abgesetzt hat, was wiederum zum gleichen Problem führt.

    Was soll hier verriegelt werden?

    Viele Grüße

    Mittwoch, 9. März 2016 15:32
  • Hi Roter Baron,
    in Deinem Beitrag vom Freitag, 29. Januar 2016 13:29 hast Du u.a. geschrieben:

    ... Wir benötigen keine Erklärung wie man mit mehreren Threads arbeitet und was im Hintergrund passiert. ...

    Dem scheint aber nicht so zu sein, wie die Diskussion zeigt. Nachfolgend habe ich mal eine kleine Demo zusammengestellt, die zeigt, wie dieser Fehler reproduziert werden kann und einen möglichen Lösungsweg zur Verhinderung des Fehlers, auch, wenn vielleicht eine andere Lösung zur Verrieglung möglich ist.

    XAML:

    <Window x:Class="WpfApplication1CS.Window23"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApplication1CS"
            mc:Ignorable="d"
            Title="Window23" Height="300" Width="400">
      <Window.Resources>
        <local:Window23VM x:Key="vm"/>
      </Window.Resources>
      <StackPanel DataContext="{Binding Source={StaticResource vm}}">
        <Button Content="Start neues Fenster, Eintrag in MainWindow" Command="{Binding Cmd}" CommandParameter="Variante1a" Margin="5" />
        <Button Content="Zugriff das Fenster, eingetragen in MainWindow" Command="{Binding Cmd}" CommandParameter="Variante1b" Margin="5" />
        <Button Content="Start und sofortiger Zugriff auf Fenster, unverriegelt" Command="{Binding Cmd}" CommandParameter="Variante2" Margin="5" />
        <Button Content="Start und sofortiger Zugriff auf Fenster, verriegelt" Command="{Binding Cmd}" CommandParameter="Variante3" Margin="5" />
      </StackPanel>
    </Window>

    Dazu der ViewModel:

    using System; using System.Threading; using System.Windows; using System.Windows.Input; namespace WpfApplication1CS { public class Window23VM { public Window23VM() { Application.Current.MainWindow = null; } public ICommand Cmd { get { return new RelayCommand(CmdExec); } } private void CmdExec(object obj) { switch (obj.ToString()) { case "Variante1a": Thread th1 = new Thread(() => { Window w1a = new Window() { Title = "Start Fenster Variante 1" }; Application.Current.Dispatcher.Invoke(() => Application.Current.MainWindow = w1a); w1a.Show(); System.Windows.Threading.Dispatcher.Run(); }); th1.SetApartmentState(ApartmentState.STA); th1.Start(); break; case "Variante1b": Window w1b = Application.Current.MainWindow as Window; Application.Current.MainWindow.Dispatcher.Invoke(() => w1b.Title = "Title Variante 1b"); break; case "Variante2": try { Thread th2 = new Thread(() => { Window w2a = new Window() { Title = "Start Fenster Variante 2" }; Application.Current.Dispatcher.Invoke(() => Application.Current.MainWindow = w2a); w2a.Show(); System.Windows.Threading.Dispatcher.Run(); }); th2.SetApartmentState(ApartmentState.STA); th2.Start(); // Window w2b = Application.Current.MainWindow as Window; Application.Current.MainWindow.Dispatcher.Invoke(() => w2b.Title = "Title Variante 2"); } catch (Exception ex) { MessageBox.Show(ex.Message); } break; case "Variante3": AutoResetEvent are = new AutoResetEvent(false); WaitHandle wh = are; Thread th3a = new Thread(() => { Window w3a = new Window() { Title = "Start Fenster Variante 3" }; Application.Current.Dispatcher.Invoke(() => Application.Current.MainWindow = w3a); w3a.Show();              are.Set();
    System.Windows.Threading.Dispatcher.Run(); }); th3a.SetApartmentState(ApartmentState.STA); th3a.Start(); Thread th3b = new Thread(() => { wh.WaitOne(); Application.Current.Dispatcher.Invoke(() => { Window w3b = Application.Current.MainWindow as Window; Application.Current.MainWindow.Dispatcher.Invoke(() => w3b.Title = "Title Variante 3"); }); }); th3b.Start(); break; default: break; } } } }



    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut



    Freitag, 11. März 2016 08:37
  • Hallo und vielen Dank für das Lehrbeispiel.

    Leider hilft das nicht.
    Das "Lehrbeispiel"-Beispiel hat das gleiche Problem und läuft für diesen Trivial-Fall in vielen Fällen durch.

    Das Beispiel geht davon aus, dass die Invoke calls durchgehen.
    Genau das ist aber nicht der Fall, weil der Dispatcher noch nicht gestartet werden konnte, da genau dieser Thread mit diesem Dispatcher kurz vor dem Run verdrängt wurde.

    Das Problem sind die Zeitscheiben z.B. zwischen.

    are.Set();

    und System.Windows.Threading.Dispatcher.Run();


    Da hilft auch keine Verriegelung.
    Vielen Dank aber wir haben das Problem ausreichend verstanden.

    Freitag, 11. März 2016 09:17
  • Hi Roter Baron,
    vielen Dank für die Rückmeldung. Du hast recht bezüglich der Möglichkeit des Zeitscheibenwechsels vor Beendigung des Run-Vorganges. Ich kann jetzt aber nicht überschauen, ob das ein Problem bezüglich des möglicherweisen parallelen Invokes des MainWindow.Dispatcher ist. Vielleicht kannst Du das nochmal prüfen und hier posten.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut


    Freitag, 11. März 2016 09:55
  • Hallo,

    das spielt keine Rolle warum.
    Fakt ist, dass der Invoke hängen bleibt, wenn der Run noch nicht ausgeführt werden konnte.
    Egal durch welchen Grund.

    Das kann nicht mit Verriegelung gelöst werden. Im Gegenteil Dein "Lehrbeispiel" mit Verriegelung läuft in einen klassischen Deadlock in diesem Szenario.

    Viele Grüße



    Freitag, 11. März 2016 11:15
  • Hi Roter Baron,
    interessant wäre zu wissen, warum Speicher nicht freigegeben wird, wenn auch recht geringfügig. Ich habe den Vorgang jetzt 1000 Mal laufen lassen und es ist eine geringfügige Erhöhung des genutzten Speichers zu verzeichnen.

    Pro Durchlauf bleiben ca. 140 Byte hängen.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut


    Freitag, 11. März 2016 15:09