2

I have following Application Structure:

public partial class MainWindow : Window
{
    // Methos to update the TextBlock
    public void updateTextBlock(string txt)
    {
        this.myTextBlock.Text += txt;
    }        

    private void startThreadBtn_Click(object sender, RoutedEventArgs e)
    {
        // Start Thread1 here 
        Thread1 thread1 = new Thread1();
        new Thread(new ThreadStart(thread1.doSomthing)).Start(); 
    }
}

class Thread1
{
    public void doSomthing()
    {
        // ------------------------------
        // Update myTextBlock from here 
        // ------------------------------

        // Thread2 starts here 
        Thread2 thread2 = new Thread2();
        new Thread(new ThreadStart(thread2.doSomthing)).Start(); 
    }
} 

class Thread2
{
    public void doSomthing()
    {
        // ------------------------------
        // Update myTextBlock from here 
        // ------------------------------
    }
}

From both of these threads Thread1 and Thread2 classes I want to update the TextBlock which is in the MainWindow.

I have seen following solutions, and don't find this condition covered in this questions, also i am beginner and find it way difficult to understand.

I can use solutions provided in the above questions for Thread1 but what about updating UI from Thread2.
I am using .NET framework 4.5.
What is the best way of doing it.

Community
  • 1
  • 1
User7723337
  • 11,857
  • 27
  • 101
  • 182
  • 1
    It's absolutely the same. – Sinatr Oct 10 '14 at 06:53
  • Can you please point the solution. – User7723337 Oct 10 '14 at 06:54
  • Since you're inside the Window class, you can use the Dispatcher. Something like if(!Dispatcher.CheckAccess()) { Dispatcher.BeingInvoker(...); }. I would however recommend you look at TPL (Task Parallel Library) to start your threads. It's much better for what you're doing since you can easily chain tasks together and moreover, use the SynchronizationContext to switch context back and forth between foreground (UI) and background threads. – kha Oct 10 '14 at 06:57

1 Answers1

4

…thread within a thread…

It appears that you have some misconceptions about what threads are. Let's look at these:

  • Threads are not executed "within" one another. They are not nested. It is more accurate to think of threads as being executed "side-by-side".

    It doesn't matter how and where you started the second thread. It's simply a thread, just like your first thread, so when you want to update the UI from within one of these (non-UI) threads, you do exactly the same thing in both cases: schedule your UI update code on the UI thread, which is done in WPF via the Dispatcher.Invoke or Dispatcher.InvokeAsync methods.

    myTextBlock.Dispatcher.Invoke(() => { … /* update myTextBlock here */ });
    //                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //                          this will be scheduled on the correct (UI) thread
    

    See also: How do I get the UI thread Dispatcher?

  • Threads are not the same as your Thread1 and Thread2 objects. Think of a thread as representing an execution path through your code; or a method that is executed on its own, if you prefer. A thread has an entry point (a method, represented by a ThreadStart delegate), which is where its execution starts, and the thread terminates when it reaches the end of that thread.

    class Thread1
    {
        … void doSomthing()
        {
            …
            Thread2 thread2 = new Thread2();
            new Thread(new ThreadStart(thread2.doSomthing)).Start(); 
        }
    }
    

    Even though you named your classes Thread1 and Thread2, that does not make them threads. They are regular .NET classes that happen to contain the methods used as your threads' entry points. Threads are represented only by the Thread class (and take note that your classes do not, and could not, inherit from Thread). And again, it doesn't matter where you happen to .Start() them; they are independent from one another.

  • When you have something like this:

    class A { … }
    class B { void Foo() { var a = new A(); … }
    

    B only takes a part in where and when an instance of A gets created, but it doesn't determine "where" A is. (Classes do not really have a location, anyway.) So it is wrong to say that "A is within B". (The type A is referred to by a method of B, and a refers to an object instance that got created during the execution of an instance method of B, but it is still completely independent from B.)


Update: Concerning your added question below about how to make myTextBlock known to your threads, there are two solutions:

  1. Either move both doSomthing methods into the MainWindow class (and give them unique names).

  2. Pass myTextBlock to your Thread1 and Thread2 objects:

    class Thread1
    {
        public Thread1(TextBlock textBlockToBeUpdated)
        {
            this.textBlock = textBlock;
        }
    
        private readonly TextBlock textBlock;
    
        void doSomthing()
        {
            textBlock.Dispatcher.Invoke(() => { /* update textBlock here */ });
        }
    }
    
    …
    
    class MainWindow
    {
        … void startThreadBtn_Click(…)
        {
            Thread1 thread1 = new Thread1(myTextBlock); // <--
            …
        }
    }
    
Community
  • 1
  • 1
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
  • You might also want to explain how and why the UI thread is different, and why you "can't" manipulate UI controls from non-UI threads. – Luaan Oct 10 '14 at 07:44
  • `InvokeAsync` is better than `BeginInvoke`. – Sinatr Oct 10 '14 at 07:46
  • @Luaan: I could, but that would be answering a different question. – stakx - no longer contributing Oct 10 '14 at 07:46
  • @Sinatr: I'm taking your word for it! :) Updated my answer. – stakx - no longer contributing Oct 10 '14 at 07:52
  • But how do i access `myTextBlock` in Thread1 and Thread2 classes, to call `myTextBlock.Dispatcher.Invoke(() => { … /* update myTextBlock here */ });`. do i need to pass the TextBlock reference while creating them, is there any other solution to it rather than passing the TextBlock reference. Can you post sample code for the same. – User7723337 Oct 10 '14 at 08:07
  • If `myTextBlock` is declared in `MainWindow`, then it won't be accessible outside of a particular instance of that class (unless you pass this `TextBlock` object to your `Thread*` classes via some parameter). The simplest solution would be to move both of your `doSomthing` methods into `MainWindow`. (Note that your latest question is not related to threading; it simply has to do with member accessibilty rules of the C# language and the .NET platform). See the update appended to my answer. – stakx - no longer contributing Oct 10 '14 at 08:19
  • One more thing, if I have multiple UI elements that I need to update from different threads, then do I need to use the same method of passing object to thread and call `Dispatcher.Invoke()`? or is there any other way? – User7723337 Oct 13 '14 at 05:41
  • If the UI elements you want to update have all been declared together (e.g. all in the same XAML file), then you can call `.Dispatcher.Invoke` on either one of them, or on their commmon parent UI control (e.g. the `Window` or `Page`); then inside the passed delegate, update all of your UI elements in one go. There's no need to call `.Dispatcher.Invoke` separately for each and every UI element. – stakx - no longer contributing Oct 13 '14 at 07:22