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

假设我有一个叫做download的方法

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

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

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

预期的方法类型错误。

我如何通过参数ThreadStart与目标方法参数?


当前回答

最简单的就是

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

这样做(相对于ParameterizedThreadStart)的优点是可以传递多个参数,并且可以进行编译时检查,而不需要一直从object进行强制转换。

其他回答

最简单的就是

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

这样做(相对于ParameterizedThreadStart)的优点是可以传递多个参数,并且可以进行编译时检查,而不需要一直从object进行强制转换。

你需要为带参数的线程方法使用ParameterizedThreadStart委托。(或者实际上根本没有,让Thread构造函数来推断。)

使用示例:

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

thread.Start(filename)

你也可以这样委派…

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

在额外的

    Thread thread = new Thread(delegate() { download(i); });
    thread.Start();

根据你的问题…

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

…对于你遇到的错误,你必须纠正你的代码

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

to

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

然而,这个问题比最初看起来的要复杂得多。

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

这个问题的相关构造函数是:

public Thread(ThreadStart start);

and

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

or

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

Casting is something you typically do not want to do. What if someone passes something else which is not a string? As this seems not possible at first (because It is my method, I know what I do or The method is private, how should someone ever be able to pass anything to it?) you may possibly end up with exactly that case for various reasons. As some cases may not be a problem, others are. In such cases you will probably end up with an InvalidCastException which you probably will not notice because it simply terminates the thread.

作为一种解决方案,您可能希望获得一个通用的ParameterizedThreadStart委托,如ParameterizedThreadStart<T>,其中T将是您想传递到Work方法的数据类型。不幸的是,这样的东西还不存在(目前?)

然而,对于这个问题有一个建议的解决方案。它涉及到创建一个类,其中包含传递给线程的数据以及表示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();

因此,通过这种方式,您可以简单地避免强制转换,并以类型安全的方式向线程提供数据;-)