2009-10-27 11 views
6

Ich habe einige Threading-Probleme mit einer großen App, an der ich gerade arbeite (Cross-Thread-Ausnahmen erhalten). Gibt es eine Möglichkeit, den Thread-Namen/ID, auf dem ein bestimmtes Steuerelement erstellt wurde, zu finden?Gibt es eine Möglichkeit, den Besitzer-Thread eines Steuerelements zu finden?

Der Fehler tritt auf, wenn ich ein neues Steuerelement der Steuerelementkollektion meines Steuerelements hinzufügen möchte. Ich kann nicht wirklich ein kleines, reproduzierbares Sample erstellen, also werde ich es so gut wie möglich beschreiben.

Ich habe ein Hauptsteuerelement, das auf einem Formular sitzt, nennen Sie es _mainControl. In seinem Konstruktor instanziiert ich eine Instanz einer anderen Steuerung, so etwas wie

ChildControl _childControl = new ChildControl(); 

Jetzt _childControl existiert, aber ich füge es nicht zu _mainControls Sammlung noch.

Schließlich erhält _mainControl eine Ereignisbenachrichtigung, dass ich das Steuerelement hinzufügen sollte. Ich überprüfen, ob this.InvokeRequired im Ereignishandler und wenn es ist, rufe ich den Handler, etwa wie folgt:

AddControlEventHander(...) 
{ 
    if(InvokeRequired) 
    { 
     BeginInvoke(new MethodInvoker(AddControlEventHander); 
     return; 
    } 
    Controls.Add(_childControl); 
} 

Die Ausnahme immer auf Controls.Add ("Cross-Thread-Betrieb nicht gültig geworfen wird: Kontrollieren Sie '_item', auf das von einem anderen Thread als dem Thread zugegriffen wurde, auf dem es erstellt wurde ").

Nun, was ich nicht verstehe, ist, wie das möglich ist. Ich habe _childControl für denselben Thread erstellt, auf dem _mainControl erstellt wurde. Wenn ich das Threads-Fenster während des Debuggens anschaue, ist der aktuelle Thread-Name/ID derselbe, wenn ich Control.Add so aufrufe, wie es war, als das _childControl hinzugefügt wurde. Die Sache, die mich am meisten verwirrt, sind die folgenden Aufrufe von _mainControl:

Wie ist das möglich? Ist es möglich, dass _childControl in einem Thread erstellt wird, während seine Children irgendwie auf einem anderen erstellt werden? Alle untergeordneten childControl-Objekte werden während der Initialisierung wie normalerweise erstellt.

Wenn jemand irgendwelche Tipps/Vorschläge hat, was passieren könnte, lass es mich wissen.

Danke.

Edit:

Falls jemand interessiert ist, fand ich heraus, was los war. Ich war neugierig, wie ein Steuerelement in einem Thread erstellt werden kann und dass untergeordnete Elemente in einem anderen Thread erstellt werden, obwohl die InitializeComponent-Komponente in demselben Thread ausgeführt wurde. Also habe ich herausgefunden, in welchem ​​Thread das Kind erstellt wurde, indem er einen Code verwendet, der ähnlich dem ist, was Charles unten vorgeschlagen hat. Sobald ich das wusste, wusste ich zumindest, auf welchen Thread ich mich konzentrieren sollte. Dann habe ich das OnHandleCreated-Ereignis der Kindersicherung abgebrochen und das Problem gefunden.

Eine Sache, die ich nicht wusste war, dass das Handle eines Steuerelements erstellt wird, wenn das Steuerelement zuerst sichtbar gemacht wird, nicht wenn es erstellt wird. Ein Thread, dem das Steuerelement nicht gehörte, versuchte also, seine Sichtbarkeit auf "wahr" zu setzen. Also fügte ich einen Check hinzu, um zu sehen, ob InvokeRequired und dachte, dass das den Trick machen würde. Etwas, das ich wirklich nicht erwartet habe, ist, dass das Aufrufen von InvokeRequired das Handle des Steuerelements erstellt, wenn es noch nicht erstellt wurde! Dies führt dazu, dass das Steuerelement im falschen Thread erstellt wird und für InvokeRequired immer den Wert false zurückgibt. Ich habe dies durch Berühren der Handle-Eigenschaft des Steuerelements gelöst, sodass es erstellt wird, bevor InvokeRequired aufgerufen wird.

Vielen Dank für die Hilfe Jungs :)

+0

Vielen Dank für den Hinweis. Ich entdeckte ebenfalls, dass das harmlose 'if (control.Handle! = Null) ... 'tatsächlich die Kontrolle über diesen Thread erzeugt! –

+0

Siehe meine Antwort hier http://stackoverflow.com/questions/8331144/ensuring-that-child-controls-are-created-in-main-ui-thread/17054689#17054689 –

Antwort

3

den Besitzer-Thread für eine Kontrolle zu bekommen, versuchen Sie dies:

private Thread GetControlOwnerThread(Control ctrl) 
{ 
    if (ctrl.InvokeRequired) 
     ctrl.BeginInvoke(
      new Action<Control>(GetControlOwnerThread), 
      new object[] {ctrl}); 
    else 
     return System.Threading.Thread.CurrentThread; 
} 

Can Kind Kontrollen von der übergeordneten (Container Control) auf einem anderen Thread sein? Ja, alles hängt davon ab, welcher Thread ausgeführt wurde, als das Steuerelement erstellt wurde (neu)

Sie müssen immer InvokeRequired überprüfen ... Da Sie nie wissen, welcher Thread in der Methode, die Sie codieren, möglicherweise aufgerufen wird. .. Ob Sie InvokeRequired für jedes untergeordnete Steuerelement separat überprüfen müssen, hängt davon ab, wie sicher Sie sind, dass alle Steuerelemente für denselben Thread erstellt wurden oder nicht. Wenn alle Steuerelemente beim Erstellen des Formulars in der gleichen Initialisierungsroutine erstellt werden, können Sie wahrscheinlich davon ausgehen, dass sie alle im selben Thread erstellt wurden.

+0

Hmm. Welche Art von Überprüfungen sind erforderlich, wenn Sie überprüfen möchten, ob InvokeRequired für ein Steuerelement benötigt wird oder nicht? Müssen Sie InvokeRequired für das Steuerelement und alle untergeordneten Elemente immer überprüfen? – Flack

+0

Ja, Sie müssen immer InvokeRequired überprüfen ... Weil Sie nie wissen, welcher Thread in die von Ihnen zu programmierende Methode eingreifen könnte ... Ob Sie InvokeRequired für jedes untergeordnete Steuerelement separat überprüfen müssen, hängt davon ab, wie sicher Sie alle sind Die Steuerelemente wurden für denselben Thread erstellt oder nicht. Wenn alle Steuerelemente beim Erstellen des Formulars in derselben Initialisierungsroutine erstellt werden, können Sie wahrscheinlich davon ausgehen, dass sie alle im selben Thread erstellt wurden. –

+0

Das scheint mir so seltsam. Soweit ich das beurteilen kann, sehen _childControl und alle seine Kinder so aus, als wären sie in demselben Thread erstellt worden. Wenn _childControls-Children irgendwie in einem anderen Thread erstellt wurden, sehe ich nicht, wie ich _Controls.Add von _mainControl aufrufen kann, da es fehlschlägt, wenn das _childControl-Handle aufgrund des Cross-Threading erstellt wird. Entweder scheitere ich wegen _childControl oder wegen seiner Kinder. Ich sehe nicht, wie das möglich ist. Ich werde es noch einmal besuchen und Refactoring betreiben. Vielleicht wird es einfach weggehen :) – Flack

Verwandte Themen