ThreadStart with Parameter方法

时间:2011-10-18 19:45:40

标签: c# winforms multithreading

我正在尝试在.Net中学习线程。

你们中的许多人一定见过这个:

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

它工作正常,但是如果我的循环方法中有参数,例如:

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

然后如何在ThreadStart中调用此方法,因为ThreadStart只接受方法名称。那么如何在不同的线程中调用循环方法呢?

5 个答案:

答案 0 :(得分:2)

您使用的是ParameterizedThreadStart: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; });
    }
}

答案 1 :(得分:2)

有一个ParameterizedThreadStart类,当实例化一个线程时,可以强制转换具有单个参数的Delegates:

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

如果你不需要对线程的启动和停止时间进行非常细粒度的控制,你可以将它留给ThreadPool并使用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;
}

答案 2 :(得分: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;
    }
}

请注意,loop方法必须接受object参数,因此您必须将object转换为您的类型。如果您不想要,可以使用闭包和匿名方法:

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();

通过这种方式,匿名方法将在str周围“关闭”,并且它将类似,就像您传递了参数一样。 类似因为关闭变量存在差异/问题。实际上我会写类似的东西:

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

这样其他任何人都无法“触摸”str2

如果您需要,我可以在SO上找到解释这个“问题”的答案

答案 3 :(得分:1)

看看ParameterizedThreadStart,它允许您将参数传递给线程启动函数。

答案 4 :(得分:0)

像这样:

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

你甚至不必乱用ThreadStart / ParameterizedThreadStart。