321

How to pass parameters to Thread.ThreadStart() method in C#?

Suppose I have method called 'download'

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

Now I have created one thread in the main method:

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

error method type expected.

How can I pass parameters to ThreadStart with target method with parameters?

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Swapnil Gupta
  • 8,751
  • 15
  • 57
  • 75
  • 3
    Check out [this](http://www.yoda.arachsys.com/csharp/threads/) article written by Jon Skeet The Parameters section is on the next page but the article as a whole is a pretty good read. – codingbadger Jul 29 '10 at 08:18

10 Answers10

760

The simplest is just

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

The advantage(s) of this (over ParameterizedThreadStart) is that you can pass multiple parameters, and you get compile-time checking without needing to cast from object all the time.

Community
  • 1
  • 1
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 18
    I'm sorry for offtopic but what does '()' operator means? I see it sometimes but i have no time to check. – Łukasz W. Jul 29 '10 at 08:25
  • 28
    It's a lambda expression with no arguments. – Noldorin Jul 29 '10 at 08:27
  • 32
    @ŁukaszW.pl - what Noldorin said ;p in C# 2.0 an alternative construct (for this example) is `new Thread(delegate() { download(filename); });` – Marc Gravell Jul 29 '10 at 08:29
  • 2
    Now I get it.. thanks.. I didn't realize that the lambda expressions are in fact returning delegates :) – Łukasz W. Jul 29 '10 at 08:32
  • 1
    @ŁukaszW.pl - they don't *always* become delegates ;p Hence my "for this example" caveat. They can also be compiled into `Expression` trees. – Marc Gravell Jul 29 '10 at 08:42
  • In this method parameters are passed as references so if you change 'filename' before the thread is actually started then the new value will be used. – tymtam Mar 02 '12 at 03:37
  • 7
    @Tymek that's not *quite* accurate; any variables captured are treated as full *lexical closures*, which (as an implementation detail) is implemented as fields on a compiler-generated class. Additionally, the closure scope is defined to be the declaration scope. It isn't really "as references" as such ("pass by reference" and "reference types" are both well defined, and neither really describes this scenario) – Marc Gravell Mar 02 '12 at 07:39
  • 5
    @MarcGravell - you are correct. All I should have said is that one should be aware that if the 'filename' changes before the thread starts the new value will be used. I shouldn't have been blabbering about the mechanics of it and I definitely shouldn't be talking about referencing. – tymtam Mar 06 '12 at 02:22
  • This is a beautiful solution, as my own thread function has three parameters. What is the non-lambda expression equivalent? Would I have to create a class to hold the parameters and pass it as an object with ParameterizedThreadStart and then cast it back to the class inside the function? Might as well go back to void pointers at that point! – Matthew Alpert Dec 12 '12 at 09:11
  • Isn't it a solution only for .net 4.5? () => method(arg) don't work at pervious .net – Secret Jan 09 '13 at 14:39
  • 1
    @Oleg that isn't a .NET version issue; it is a compiler version issue - and that should work from C# 3 onwards – Marc Gravell Jan 09 '13 at 15:07
  • @MarcGravell But, don't MS make pair, C# 5.0 won't work with the net 2.0, any new versions of C# compiler requires new .net fw version. Am I right? – Secret Jan 09 '13 at 16:29
  • @Oleg nope. C# 5 can target everything from .NET 2.0 onwards. All the compilers (not including 1.1) have been able to target previous framework versions. They can probably target higher framework versions too, if you use the command line (I haven't tried). – Marc Gravell Jan 09 '13 at 16:37
  • The example provided in this answer is not thread safe. The value of "filename" could change before the task starts, leading to an indeterminate result. – MonkeyWrench Feb 20 '15 at 22:16
  • @MonkeyWrench well, in the example shown, `filename` is never assigned any other values ;p Yes, it is entirely true to say that captured variables are subject to threading issues, but there are ways of avoiding that - they do, however, require concrete examples. – Marc Gravell Feb 20 '15 at 22:24
  • 1
    @Mark, from what I understand from your last comment, if I use this syntax to spawn 20 Thread in a for loop and pass for loop index to thread as ID, all my threads will have same ID, right? should I assume the delegate version (.NET 2) is same? – AaA Oct 04 '15 at 12:05
  • 1
    @AaA that depends very much on the exact scenario, sadly - in particular, where the captured variable is declared **exactly** - which for some things (`foreach`, as a key example) actually changes between C# versions (in recent C# versions, it's scope is *inside* the loop, so you don't get capture problems; in older versions it is *outside* the loop) – Marc Gravell Oct 05 '15 at 13:22
  • Could you explain what this part means/does? `() =>` What is the difference when using it and when not using it? – tedi Mar 07 '18 at 14:19
  • 1
    @JedatKinports `() =>` denotes a lambda expression (like an anonymous method) that takes no parameters – Marc Gravell Mar 07 '18 at 17:18
  • @user662285 there are almost no scenarios in which you should stop a thread from external code. Let the thread stop itself by running to completion, perhaps by checking an exit flag periodically and breaking out of whatever loop it is running – Marc Gravell Dec 30 '18 at 11:15
44

Look at this example:

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

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

You are first creating a thread by passing delegate to worker method and then starts it with a Thread.Start method which takes your object as parameter.

So in your case you should use it like this:

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

But your 'download' method still needs to take object, not string as a parameter. You can cast it to string in your method body.

Josh
  • 3
  • 2
Łukasz W.
  • 9,538
  • 5
  • 38
  • 63
28

You want to use the ParameterizedThreadStart delegate for thread methods that take parameters. (Or none at all actually, and let the Thread constructor infer.)

Example usage:

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

thread.Start(filename)
Noldorin
  • 144,213
  • 56
  • 264
  • 302
8

You could also delegate like so...

ThreadStart ts = delegate
{
      bool moreWork = DoWork("param1", "param2", "param3");
      if (moreWork) 
      {
          DoMoreWork("param1", "param2");
      }
};
new Thread(ts).Start();
bluish
  • 26,356
  • 27
  • 122
  • 180
Magic Mick
  • 1,475
  • 1
  • 21
  • 32
4

In Additional

    Thread thread = new Thread(delegate() { download(i); });
    thread.Start();
Metin Atalay
  • 1,375
  • 18
  • 28
4

I would recommend you to have another class called File.

public class File
{
   private string filename;

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

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

And in your thread creation code, you instantiate a new file:

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);
3

You can encapsulate the thread function(download) and the needed parameter(s)(filename) in a class and use the ThreadStart delegate to execute the thread function.

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);
Jackypengyu
  • 251
  • 3
  • 5
  • I like this approach much better, I found that the lambda expression approach is not always keeping track of the right parameters – Steven Combs Jan 01 '18 at 20:33
0

How about this: (or is it ok to use like this?)

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

According to your question...

How to pass parameters to Thread.ThreadStart() method in C#?

...and the error you encountered, you would have to correct your code from

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

to

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



However, the question is more complex as it seems at first.

The Thread class currently (4.7.2) provides several constructors and a Start method with overloads.

These relevant constructors for this question are:

public Thread(ThreadStart start);

and

public Thread(ParameterizedThreadStart start);

which either take a ThreadStart delegate or a ParameterizedThreadStart delegate.

The corresponding delegates look like this:

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

So as can be seen, the correct constructor to use seems to be the one taking a ParameterizedThreadStart delegate so that some method conform to the specified signature of the delegate can be started by the thread.

A simple example for instanciating the Thread class would be

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

or just

Thread thread = new Thread(Work);

The signature of the corresponding method (called Work in this example) looks like this:

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

What is left is to start the thread. This is done by using either

public void Start();

or

public void Start(object parameter);

While Start() would start the thread and pass null as data to the method, Start(...) can be used to pass anything into the Work method of the thread.

There is however one big problem with this approach: Everything passed into the Work method is cast into an object. That means within the Work method it has to be cast to the original type again like in the following example:

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.

As a solution you would expect to get a generic ParameterizedThreadStart delegate like ParameterizedThreadStart<T> where T would be the type of data you want to pass into the Work method. Unfortunately something like this does not exist (yet?).

There is however a suggested solution to this issue. It involves creating a class which contains both, the data to be passed to the thread as well as the method that represents the worker method like this:

public class ThreadWithState
{
    private string message;

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

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

With this approach you would start the thread like this:

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

thread.Start();

So in this way you simply avoid casting around and have a typesafe way of providing data to a thread ;-)

Markus Safar
  • 6,324
  • 5
  • 28
  • 44
-2

here is the perfect way...

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);
}
Aylian Craspa
  • 422
  • 5
  • 11