views:

79

answers:

4

Hello

I created the following project to show you guys how I plan to do things. But my main project will be bigger and will have multiple classes. I'm just trying to get this to work properly so I know I'm using good practice when coding.

ok, lets begin :), so my form has a button called "button1" and a text box called "textBox1" and I have a class called "Class1" which has a method "testtest()", I just put Thread.Sleep in the testtest method so I can find out its running on another thread.

Here is my form code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Test
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    delegate void Class1Deligate();

    private void button1_Click(object sender, EventArgs e)
    {
      Class1 c = new Class1();
      Class1Deligate testMethod = new Class1Deligate(c.testtest);
      testMethod.BeginInvoke(endTestMethod, testMethod);
    }

    void endTestMethod(IAsyncResult ar)
    {

    }
  }
}

and here is my Class one code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Test
{
  class Class1
  {
    public void testtest()
    {
      Thread.Sleep(8888);
      MessageBox.Show("Done");
    }
  }
}

Am I creating a new thread correctly? And can someone please tell me how to update textbox1 from the testtest method in class1 while its running? I made a earlier post and I was told to use Dispatcher, but for some reason it seems the Dispatcher class is not available for me.

Best Regards

+1  A: 

I don't think you're actually starting a new thread. delegate.BeginInvoke probably uses the internal ThreadPool to do it's bidding. And as a side note, I don't think you should call BeginInvoke without the appropriate EndInvoke call after it (otherwise the reference to the IAsyncResult returned from the BeginInvoke call won't be released).

If you want a new thread you could create a new one using the Thread class.

Thread t = new Thread(c.testtest);
t.IsBackground = true; // end if main thread ends
t.Start();

Note that if you use the Thread class like above, and using GUI, you need to attach a method to the GUI thread to update it. You can do that using the Control.Invoke call

public void updateGUI(object state) {
    if (control.InvokeRequired) {
        control.Invoke(new Action<object>(updateGUI), state);
        return;
    }
    // if we are here, we can safely update the GUI
}
Patrick
I'm not sure if I want a new thread or not, cause in some examples I see the Thread class is used and in others I see BeginInvoke is used, so its really confusing me.
Arya
@Arya: Well some start a new thread, some use the backgroundworker, some use the ThreadPool. It's up to you to determine what fits best at the time you are using it. All methods have their pros and cons.
Patrick
A: 

Firstly, there is the ThreadPool class available for threading if you like. It is what I generally use to fire off new threads e.g.,

ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);

The link explains a bit more about them. As for the GUI, you will need to do a check to see whether the current thread is the same thread as the UI thread (as only the UI thread can update the UI), and if it isn't, invoke a function on the UI thread. This can be done like so,

delegate void ChangeFormUnsafeCallback(string text);
private void ChangeFormUnsafe(string text)
{
  // Do stuff to form/controls
}

private void SomeFunction()
{      
  myForm.Invoke(new ChangeFormUnsafeCallback(ChangeFormUnsafe), "foo");
}

You can do a check to see if the invoke is required, however it's usually pretty straight forward to know whether the function will be used within the UI thread or not and Invoke appropriately. As a general rule of thumb, unless your function is being run as a result of something happening on the form (e.g., button being clicked) you will need to use the Invoke function.

mrnye
@mrnye: You know whether or not you need to use Invoke by checking `InvokeRequired`... And you don't *need* to declare a new delegate, you can use `Action<T>`.
Patrick
@Patrick Yeah I know you can check with InvokeRequired, I was just saying that 99% of the time you will require an invoke because only UI-fired events will run on the UI thread.
mrnye
A: 

Arya

Using your approach (async delegates), you are not starting a new thread, but you are using a thread from the .net threadpool, which will be released when your work is done. The correct way to invoke the delegate and be calledback when finished is as follows:

    void button1_Click(object sender, EventArgs e)
    {
        Class1 c = new Class1();
        Action action = c.Test;

        action.BeginInvoke(new AsyncCallback(EndTestMethod), null);
    }
    void EndTestMethod(IAsyncResult token)
    {    
        Action callBack = (Action)((AsyncResult)token).AsyncDelegate;
        callBack.EndInvoke(token);
    }

The asynchronous delegate approach is useful if your method is fairly short lived and you require a callback. Otherwise, for a longer running process you may want to create and start a new thread.

 Thread t = new Thread(c.Test);
        t.IsBackground = true;
        t.Start();

However, signalling back to the calling thread, via a callback, will become more long-winded as you will have to write this code yourself.

Regarding the Dispatcher, this is a WPF class which queues actions to be handled by the UI thread in WPF applications (amongst other things). From your code, it looks like you are writing a Winforms application, so this type isn't appropriate.

Finally, for updating the text box, you need to check whether you are trying to access that control on the same thread that created the control (UI thread). This is is done via control.invokerequired and control.invoke. As Follows:

       void AddText(string text)
    {
        if (textBox1.InvokeRequired)
        {
            textBox1.Invoke((Action<string>)AddText, text);
        }
        textBox1.Text = text;
    }
Arthur Raffles