Как передать параметры в ThreadStart метод в Thread?

291

Как передать параметры Thread.ThreadStart()метода в C #?

Предположим, у меня есть метод под названием «скачать»

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

Теперь я создал один поток в основном методе:

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

ожидаемый тип ошибки

Как я могу передать параметры ThreadStartцелевому методу с параметрами?

Свапнил гупта
источник
2
Прочтите эту статью, написанную Джоном Скитом. Раздел «Параметры» находится на следующей странице, но статья в целом довольно хорошо читается.
codingbadger

Ответы:

696

Самое простое просто

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

Преимущество (преимущества) этого (пере ParameterizedThreadStart) состоит в том, что вы можете передавать несколько параметров, и вы получаете проверку во время компиляции, не требуя приведения objectвсе время.

Марк Гравелл
источник
15
Прошу прощения за оффтоп, но что означает оператор ()? Я вижу это иногда, но у меня нет времени, чтобы проверить.
ŁukaszW.pl
24
Это лямбда-выражение без аргументов.
Нолдорин
31
@ ŁukaszW.pl - что сказал Нолдорин; p в C # 2.0 альтернативная конструкция (для этого примера)new Thread(delegate() { download(filename); });
Марк Гравелл
7
@Tymek, это не совсем точно; любые захваченные переменные обрабатываются как полные лексические замыкания , которые (как детали реализации) реализуются как поля в сгенерированном компилятором классе. Кроме того, область закрытия определяется как область объявления. Это не совсем «как ссылки» как таковые («передача по ссылке» и «ссылочные типы» хорошо определены, и ни один из них не описывает этот сценарий)
Марк Грэвелл
5
@MarcGravell - вы правы. Все, что я должен был сказать, это то, что нужно знать, что если имя файла изменяется до начала потока, будет использоваться новое значение. Я не должен был болтать о механике этого, и я определенно не должен говорить о ссылках.
тымтам
37

Посмотрите на этот пример:

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

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

Сначала вы создаете поток, передавая делегат в рабочий метод, а затем запускаете его с помощью метода Thread.Start, который принимает ваш объект в качестве параметра.

Так что в вашем случае вы должны использовать это так:

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

Но ваш метод загрузки все равно должен принимать объект , а не строку в качестве параметра. Вы можете привести его к строке в теле вашего метода.

ŁukaszW.pl
источник
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();
Мастер Мик
источник
4

В дополнение

    Thread thread = new Thread(delegate() { download(i); });
    thread.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);
Jackypengyu
источник
Мне гораздо больше нравится этот подход, я обнаружил, что подход лямбда-выражений не всегда отслеживает правильные параметры
скупой
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

По твоему вопросу ...

Как передать параметры в метод Thread.ThreadStart () в C #?

... и ошибка, с которой вы столкнулись, вам придется исправить свой код с

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>где Tбудет тип данных, которые вы хотите передать в Workметод. К сожалению что-то подобное не существует (пока?).

Однако есть предложенное решение этой проблемы. Он включает в себя создание класса, который содержит как данные, которые будут переданы потоку, так и метод, который представляет рабочий метод следующим образом:

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);
}
Айлиан Краспа
источник