1

I am trying to learn Threading in .Net.

Many of you must have seen this:

private void button1_Click(object sender, EventArgs e)
{
    Thread t = new Thread(new ThreadStart(loop));
    t.Start();
}

private void loop()
{
    for (int i = 0; i < 100000; i++)
    {
        textBox1.Text = i.ToString();
    }
}

It works fine, but what if my loop method has parameters in it, like:

private void loop(string str)
{
    for (int i = 0; i < 100000; i++)
    {
        textBox1.Text = i + str;
    }
}

Then how to call this method in my ThreadStart as ThreadStart accepts just the method name. Then how to call loop method in a different thread?

Sandy
  • 11,332
  • 27
  • 76
  • 122
  • 3
    if `textBox1` is a WinForms or WPF text box, then *don't do this.* The `.Text` property can only be changed by the thread which created the `TextBox`, which is obviously not the thread that you're about to start. Look into either `BackgroundWorker` or `Control.Invoke()` (for WinForms) or `Dispatcher` (for WPF). – dlev Oct 18 '11 at 19:50

5 Answers5

2

You'd use ParameterizedThreadStart instead: http://msdn.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx

Thread t = new Thread(new ParameterizedThreadStart(loop));
t.Start("Foo");

// Note the use of Object here to match the delegate signature
private void loop(Object state)
{
    var str = state as String;
    for (int i = 0; i < 100000; i++)
    {
        // For what it is worth, this is illegal:
        // textBox1.Text = i + str;
        // You need to Invoke back to the UI thread to access a control's properties:
        textBox1.Invoke(()=> { textBox1.Text = i + str; });
    }
}
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
2

There is a ParameterizedThreadStart class that Delegates with a single parameter can be cast to when instantiating a Thread:

private void button1_Click(object sender, EventArgs e)
{
    Thread t = new Thread(new ParameterizedThreadStart(loop));
    t.Start(str);
}

private void loop(string str)
{
    for (int i = 0; i < 100000; i++)
    {
        //the code you had is a no-no when you are multithreading;
        //all UI updates must occur on the main thread
        //textBox1.Text = i + str;
        UpdateTextBoxText(textBox1, i+str);
    }
}

private void UpdateTextBoxText(TextBox textBox, string text)
{
   //the method will invoke itself on the main thread if it isn't already running there
   if(InvokeRequired)
   {
      this.Invoke((MethodInvoker)(()=>UpdateTextBoxText(TextBox textBox, string text)));
      return;
   }

   textBox.Text = text;
}

If you don't need very fine-grained control over when the thread starts and stops, you can leave it to the ThreadPool and use Delegate.BeginInvoke:

private void button1_Click(object sender, EventArgs e)
{
    Action<string> method = loop;

    method.BeginInvoke(str, null, null);
}

private void loop(string str)
{
    for (int i = 0; i < 100000; i++)
    {
        //textBox1.Text = i + str;
        UpdateTextBoxText(textBox1, i+str);
    }
}

private void UpdateTextBoxText(TextBox textBox, string text)
{
   //the method will invoke itself on the main thread if it isn't already running there
   if(InvokeRequired)
   {
      this.Invoke((MethodInvoker)(()=>UpdateTextBoxText(textBox, text)));
      return;
   }

   textBox.Text = text;
}
KeithS
  • 70,210
  • 21
  • 112
  • 164
2
Thread t = new Thread(new ParameterizedThreadStart(loop));
t.Start("Hello world");

private void loop(object obj)
{
    string str = (string)obj;

    for (int i = 0; i < 100000; i++)
    {
        // Don't do this: you can't change a control from another thread. Danger Will Robinson!
        textBox1.Text = i + str;
    }
}

Note that the loop method must accept an object parameter, so you'll have to upcast the object to your type. If you don't want, you can use a closure and an anonymous method:

string str = "Hello world";
Thread t = new Thread(() => {
    for (int i = 0; i < 100000; i++)
    {
        // Don't do this: you can't change a control from another thread. Danger Will Robinson!   
        textBox1.Text = i + str;
    }
});
t.Start();

In this way the anonymous method will "close" around str and it will be similar as if you had passed the parameter. Similar because there are differences/problems on closing variables. In reality I would write something similar to:

string str = "Hello world";

{
    string str2 = str;

    Thread t = new Thread(() => {
        for (int i = 0; i < 100000; i++)
        {
            // Don't do this: you can't change a control from another thread. Danger Will Robinson! 
            textBox1.Text = i + str2;
        }
    });

    t.Start();
}

so that no one else can "touch" str2.

If you need I can find some answer on SO that explain this "problem"

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • thanks....it solved my problem. Any further information will be greatly appreciated. – Sandy Oct 18 '11 at 20:06
  • @rapsalands Here there is *the* q/a about closures and variables http://stackoverflow.com/q/512166/613130 – xanatos Oct 18 '11 at 20:13
1

Like this:

new Thread(() => loop("MyString")).Start();

You don't even have to mess with ThreadStart/ParameterizedThreadStart.

qJake
  • 16,821
  • 17
  • 83
  • 135
1

Take a look at ParameterizedThreadStart, it allows you to pass parameters into your thread start function.

Michael Goldshteyn
  • 71,784
  • 24
  • 131
  • 181