2015-08-07 11 views
6

Właśnie odpowiedziałem na pytanie, czy Task może zaktualizować interfejs użytkownika. Kiedy grałem z moim kodem, zdałem sobie sprawę, że nie jestem pewien siebie w kilku kwestiach.Zadanie w tle może czasami aktualizować interfejs użytkownika?

Jeśli mam formularza systemu Windows z jednej kontroli txtHello na to, że jestem w stanie zaktualizować UI z zadaniem, jak się wydaje, czy od razu zrobić na Task.Run:

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

Jednak gdybym Thread.Sleep nawet dla 5 milisekund, oczekiwany CrossThread zostanie zgłoszony błąd:

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

nie jestem pewien, dlaczego tak się dzieje. Czy istnieje jakiś rodzaj optymalizacji dla wyjątkowo krótkiego czasu pracy Task?

+0

To będzie dla mnie niespodzianka, jeśli to prawda! –

Odpowiedz

6

Nie zakładać ślad stosu wyjątku, ale spodziewam się, że wyglądało to mniej więcej tak:

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 

widzimy, że jest wyjątek od nieruchomości Control.Handle pochłaniacza. I rzeczywiście, jeśli spojrzymy na source code dla tej nieruchomości nie jest, zgodnie z oczekiwaniami:

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

     if (!IsHandleCreated) 
     { 
      CreateHandle(); 
     } 

     return HandleInternal; 
    } 
} 

Interesującą częścią jest, gdy spojrzymy na kod, który wywołuje Control.Handle. W tym przypadku jest to właściwość Control.WindowText seter:

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; 
      } 
     } 
    } 
} 

Zauważ, że własność Handle jest wywoływana tylko wtedy IsHandleCreated jest true.

I pod względem kompletności, jeśli spojrzymy na kod dla IsHandleCreated widzimy następujące:

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

Więc powód nie dostaniesz wyjątek, dlatego do czasu Task zostanie uruchomiony, uchwyt okna nie został jeszcze utworzony, czego można się spodziewać, ponieważ Task rozpoczyna się w konstruktorze formularza, czyli zanim formularz zostanie wyświetlony.

Przed utworzeniem uchwytu okna modyfikowanie właściwości nie wymaga jeszcze pracy z wątku interfejsu użytkownika. W tym małym oknie czasowym na początku programu wydaje się, że możliwe jest wywoływanie metod na instancjach sterujących z wątku innego niż UI bez uzyskania wyjątku "krzyżowego". Jasne jest jednak, że istnienie tego specjalnego, małego okna czasowego nie zmienia faktu, że zawsze powinniśmy upewnić się, że metody kontrolne z wątku UI są bezpieczne.

Aby udowodnić, że decydującym czynnikiem w uzyskaniu (lub nie) wyjątku "krzyżowego" jest próba utworzenia uchwytu okna, spróbuj zmodyfikować przykład, aby wymusić utworzenie uchwytu okna przed rozpoczęciem zadania i zauważyć, jak będzie teraz konsekwentnie uzyskać oczekiwaną wyjątku, nawet bez snu:

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

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

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

stosownej dokumentacji: Control.Handle

Jeśli uchwyt nie został jeszcze stworzony, przedstawieniu nieruchomość zmusi uchwyt do utworzenia.

Powiązane problemy