Як передати параметри методу 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, це не зовсім точно; будь-які захоплені змінні трактуються як повні лексичні закриття , які (як деталізація реалізації) реалізовані як поля в класі, створеному компілятором. Крім того, область закриття визначається як область декларації. Це насправді не "посилання" як такі ("пройти посилання" та "типи посилань" обидва є чітко визначеними, і жоден із насправді не описує цей сценарій)
Marc Gravell

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

Спочатку ви створюєте потік, передаючи делегат методу delav, а потім запускаєте його методом Thread.Start, який приймає ваш об'єкт як параметр.

Тож у вашому випадку слід використовувати його так:

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

Але ваш метод "завантаження" все ж повинен використовувати параметр об'єкта , а не рядка . Ви можете передавати його на рядок у тілі методу.


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

Мені такий підхід набагато подобається, я виявив, що підхід до вираження лямбда не завжди відстежує правильні параметри
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

Відповідно до вашого запитання ...

Як передати параметри методу 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.