2015-08-07 8 views
6

Ich habe gerade eine Frage beantwortet, ob ein Task die Benutzeroberfläche aktualisieren kann. Als ich mit meinem Code spielte, wurde mir klar, dass ich mich in einigen Dingen nicht klar bin.Hintergrundaufgabe manchmal in der Lage UI zu aktualisieren?

Wenn ich ein Windows-Formular mit einer Steuer txtHello auf sie haben, ich bin in der Lage die Benutzeroberfläche von einer Aufgabe zu aktualisieren, so scheint es, wenn ich es sofort auf Task.Run:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     Task.Run(() => 
     { 
      txtHello.Text = "Hello"; 
     }); 
    } 
} 

Allerdings, wenn ich

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     Task.Run(() => 
     { 
      Thread.Sleep(5); 
      txtHello.Text = "Hello"; //kaboom 
     }); 
    } 
} 

ich, warum das passiert nicht sicher bin: Thread.Sleep sogar 5 Millisekunden werden die erwarteten CrossThread Fehler ausgelöst. Gibt es eine Art von Optimierung für einen extrem kurzen Lauf Task?

+0

Das wird für mich auch eine Überraschung sein, wenn es wahr ist! –

Antwort

6

Sie haben die Ausnahme-Stack-Trace nicht schreiben, aber ich erwarte, dass es etwas so aussah:

System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on. 
    at System.Windows.Forms.Control.get_Handle() 
    at System.Windows.Forms.Control.set_WindowText(String value) 
    at System.Windows.Forms.TextBoxBase.set_WindowText(String value) 
    at System.Windows.Forms.Control.set_Text(String value) 
    at System.Windows.Forms.TextBoxBase.set_Text(String value) 
    at System.Windows.Forms.TextBox.set_Text(String value) 
    at WindowsFormsApplicationcSharp2015.Form1.<.ctor>b__0_0() in D:\test\WindowsFormsApplicationcSharp2015\Form1.cs:line 27 

Wir können sehen, dass die Ausnahme von der Control.Handle Getter-Eigenschaft ausgelöst. Und in der Tat, wenn wir an den source code für diese Eigenschaft schauen, da ist es, wie erwartet:

public IntPtr Handle { 
    get { 
     if (checkForIllegalCrossThreadCalls && 
      !inCrossThreadSafeCall && 
      InvokeRequired) { 
      throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall, 
                  Name)); 
     } 

     if (!IsHandleCreated) 
     { 
      CreateHandle(); 
     } 

     return HandleInternal; 
    } 
} 

Der interessante Teil ist, wenn wir den Code anschauen, die Control.Handle aufruft. In diesem Fall ist, dass die Control.WindowText Set-Eigenschaft:

set { 
    if (value == null) value = ""; 
    if (!WindowText.Equals(value)) { 
     if (IsHandleCreated) { 
      UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value); 
     } 
     else { 
      if (value.Length == 0) { 
       text = null; 
      } 
      else { 
       text = value; 
      } 
     } 
    } 
} 

Beachten Sie, dass die Handle Eigenschaft wird nur aufgerufen, wenn IsHandleCreatedtrue ist.

Und für die Vollständigkeit, wenn wir den Code für IsHandleCreated betrachten, sehen wir folgende:

public bool IsHandleCreated { 
    get { return window.Handle != IntPtr.Zero; } 
} 

der Grund, warum Sie nicht die Ausnahme erhalten, ist, weil also durch die Zeit, die Task ausgeführt wird, die Fensterhandle wurde noch nicht erstellt, was zu erwarten ist, da die Task im Konstruktor des Formulars beginnt, dh bevor das Formular angezeigt wird.

Bevor das Fensterhandle erstellt wird, erfordert das Ändern einer Eigenschaft noch keine Arbeit vom UI-Thread. Während dieses kleinen Zeitfensters zu Beginn des Programms scheint es also möglich zu sein, die Methoden auf Steuerinstanzen von einem Nicht-UI-Thread aufzurufen, ohne die "Cross-Thread" -Ausnahme zu erhalten. Aber klar, die Existenz dieses speziellen kleinen Zeitfensters ändert nichts an der Tatsache, dass wir immer sicherstellen sollten, Kontrollmethoden aus dem UI-Thread als sicher aufzurufen.

Um zu beweisen, dass das Timing der Fensterhandle-Erstellung ausschlaggebend dafür ist, die "cross thread" -Ausnahme zu erhalten, versuchen Sie, das Beispiel zu ändern, um die Erstellung des Fensterhandle zu erzwingen und beachten Sie, wie Sie wird nun konsequent die erwartete Ausnahme erhalten, auch ohne Schlaf:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 

     // Force creation of window handle 
     var dummy = txtHello.Handle; 

     Task.Run(() => 
     { 
      txtHello.Text = "Hello"; // kaboom 
     }); 
    } 
} 

Relevante Dokumentation: Control.Handle

Wenn der Griff wurde noch nicht erstellt wurde, diese Eigenschaft verweist zwingen wird, die Griff erstellt werden.

Verwandte Themen