如何将参数传递给Thread中的ThreadStart方法?


290

如何将参数传递给Thread.ThreadStart()C#中的方法?

假设我有一个名为“下载”的方法

public void download(string filename)
{
    // download code
}

现在,我在main方法中创建了一个线程:

Thread thread = new Thread(new ThreadStart(download(filename));

错误方法类型预期。

如何将参数传递给ThreadStart带有参数的目标方法?


2
查阅Jon Skeet撰写的这篇文章。Parameters部分在下一页上,但是从整体上看,这是一篇不错的文章。
codingbadger

Answers:


695

最简单的就是

string filename = ...
Thread thread = new Thread(() => download(filename));
thread.Start();

(相对于ParameterizedThreadStart)的优点是,您可以传递多个参数,并且无需进行object所有转换就可以进行编译时检查。


15
抱歉,我很抱歉,但是'()'运算符是什么意思?我有时看到它,但是我没有时间检查。
ŁukaszW.pl

24
这是一个不带参数的lambda表达式。
Noldorin 2010年

31
@ŁukaszW.pl-Noldorin所说的;在C#2.0中的p(该示例)的另一种构造是new Thread(delegate() { download(filename); });
Marc Gravell

7
@Tymek 不太准确;捕获的所有变量都被视为完整的词法闭包,作为实现的详细信息,被实现为编译器生成的类上的字段。此外,将闭包范围定义为声明范围。它并不是真正的“引用形式”(“按引用传递”和“引用类型”都定义明确,并且都没有真正描述这种情况)
Marc Gravell

5
@MarcGravell-你是正确的。我只想说的是,应该知道,如果在线程启动之前“文件名”发生更改,则将使用新值。我不应该对它的机制感到困惑,我绝对不应该谈论引用。
tymtam'3

36

看这个例子:

public void RunWorker()
{
    Thread newThread = new Thread(WorkerMethod);
    newThread.Start(new Parameter());
}

public void WorkerMethod(object parameterObj)
{
    var parameter = (Parameter)parameterObj;
    // do your job!
}

首先通过将委托传递给worker方法来创建线程,然后使用Thread.Start方法启动该线程,该方法将您的对象作为参数。

因此,在您的情况下,您应该像这样使用它:

    Thread thread = new Thread(download);
    thread.Start(filename);

但是您的“下载”方法仍然需要使用object而不是string作为参数。您可以将其转换为方法主体中的字符串。


25

您要对使用ParameterizedThreadStart参数的线程方法使用委托。(或者实际上什么也没有,让Thread构造函数进行推断。)

用法示例:

var thread = new Thread(new ParameterizedThreadStart(download));
//var thread = new Thread(download); // equivalent

thread.Start(filename)

7

您也可以delegate这样...

ThreadStart ts = delegate
{
      bool moreWork = DoWork("param1", "param2", "param3");
      if (moreWork) 
      {
          DoMoreWork("param1", "param2");
      }
};
new Thread(ts).Start();


3

您可以将线程函数(下载)和所需的参数(文件名)封装在一个类中,并使用ThreadStart委托执行线程函数。

public class Download
{
    string _filename;

    Download(string filename)
    {
       _filename = filename;
    }

    public void download(string filename)
    {
       //download code
    }
}

Download = new Download(filename);
Thread thread = new Thread(new ThreadStart(Download.download);

我更好地喜欢这种方法,我发现lambda表达式方法并不总是跟踪正确的参数
Meanbunny

3

我建议您再开设一个名为File的课程。

public class File
{
   private string filename;

   public File(string filename)
   {
      this.filename= filename;
   }

   public void download()
   {
       // download code using filename
   }
}

在线程创建代码中,实例化一个新文件:

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);

0

这样如何:(或者可以这样使用吗?)

var test = "Hello";
new Thread(new ThreadStart(() =>
{
    try
    {
        //Staff to do
        Console.WriteLine(test);
    }
    catch (Exception ex)
    {
        throw;
    }
})).Start();

-1

根据您的问题...

如何在C#中将参数传递给Thread.ThreadStart()方法?

...以及您遇到的错误,您必须从以下位置更正代码

Thread thread = new Thread(new ThreadStart(download(filename));

Thread thread = new Thread(new ThreadStart(download));
thread.Start(filename);



但是,这个问题乍看起来似乎更加复杂。

Thread当前的类(4.7.2)提供了几个构造函数和一个Start带有重载的方法。

这些相关的构造函数是:

public Thread(ThreadStart start);

public Thread(ParameterizedThreadStart start);

可以选择一个ThreadStart代表或一个ParameterizedThreadStart代表。

相应的代表如下所示:

public delegate void ThreadStart();
public delegate void ParameterizedThreadStart(object obj);

可以看出,要使用的正确构造函数似乎是接受ParameterizedThreadStart委托的构造函数,以便某些方法可以由线程启动符合委托的指定签名的方法。

初始化Thread类的一个简单示例是

Thread thread = new Thread(new ParameterizedThreadStart(Work));

要不就

Thread thread = new Thread(Work);

相应方法的签名(Work在此示例中称为)如下所示:

private void Work(object data)
{
   ...
}

剩下的就是启动线程。这可以通过使用以下任一方法来完成

public void Start();

要么

public void Start(object parameter);

虽然Start()将启动线程并将其null作为数据传递给方法,但是Start(...)可以将任何东西传递Work给线程的方法。

但是,这种方法存在一个大问题:传递给该Work方法的所有内容都被转换为对象。这意味着必须在Work方法内将其再次强制转换为原始类型,如以下示例所示:

public static void Main(string[] args)
{
    Thread thread = new Thread(Work);

    thread.Start("I've got some text");
    Console.ReadLine();
}

private static void Work(object data)
{
    string message = (string)data; // Wow, this is ugly

    Console.WriteLine($"I, the thread write: {message}");
}



强制转换是您通常不希望执行的操作。

如果有人传递了不是字符串的其他东西怎么办?乍看起来似乎是不可能的(因为这是我的方法,因为我知道我在做什么,或者该方法是私有的,所以有人应该如何将任何东西传递给它?),由于各种原因,您可能最终会遇到这种情况。由于某些情况可能不是问题,其他情况则是。在这种情况下InvalidCastException,您可能会得到一个可能根本不会注意到的,因为它只是终止了线程。

作为一种解决方案,您希望获得一个通用ParameterizedThreadStart委托,例如要传递到方法中的数据类型ParameterizedThreadStart<T>在哪里。不幸的是,这样的东西不存在(还?)。TWork

但是,有针对此问题的建议解决方案。它涉及创建一个类,该类既包含要传递给线程的数据,又包含表示worker方法的方法,如下所示:

public class ThreadWithState
{
    private string message;

    public ThreadWithState(string message)
    {
        this.message = message;
    }

    public void Work()
    {
        Console.WriteLine($"I, the thread write: {this.message}");
    }
}

使用这种方法,您将像这样启动线程:

ThreadWithState tws = new ThreadWithState("I've got some text");
Thread thread = new Thread(tws.Work);

thread.Start();

因此,以这种方式,您只需避免进行转换,并拥有一种将数据提供给线程的类型安全方式;-)


-2

这是完美的方式...

private void func_trd(String sender)
{

    try
    {
        imgh.LoadImages_R_Randomiz(this, "01", groupBox, randomizerB.Value); // normal code

        ThreadStart ts = delegate
        {
            ExecuteInForeground(sender);
        };

        Thread nt = new Thread(ts);
        nt.IsBackground = true;

        nt.Start();

    }
    catch (Exception)
    {

    }
}

private void ExecuteInForeground(string name)
{
     //whatever ur function
    MessageBox.Show(name);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.