Jak przekazać parametry do metody ThreadStart w wątku?


291

Jak przekazać parametry do Thread.ThreadStart()metody w C #?

Załóżmy, że mam metodę o nazwie „pobierz”

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

Teraz utworzyłem jeden wątek w metodzie głównej:

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

oczekiwany typ błędu.

Jak mogę przekazać parametry za ThreadStartpomocą metody docelowej z parametrami?


2
Sprawdź ten artykuł napisany przez Jona Skeeta Sekcja Parametry znajduje się na następnej stronie, ale cały artykuł jest całkiem niezły.
codingbadger

Odpowiedzi:


696

Najprostsze jest po prostu

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

Zaletą tego (ponad ParameterizedThreadStart) jest to, że można przekazać wiele parametrów i uzyskać sprawdzanie czasu kompilacji bez konieczności ciągłego rzutowania object.


15
Przepraszam za offtopic, ale co oznacza operator „()”? Czasami to widzę, ale nie mam czasu na sprawdzenie.
ŁukaszW.pl,

24
To wyrażenie lambda bez argumentów.
Noldorin

31
@ ŁukaszW.pl - co powiedział Noldorin; w C # 2.0 alternatywnym konstruktem (dla tego przykładu) jestnew Thread(delegate() { download(filename); });
Marc Gravell

7
@Tymek, który nie jest całkiem dokładny; wszelkie wychwycone zmienne są traktowane jako pełne zamknięcia leksykalne , które (jako szczegół implementacji) są implementowane jako pola w klasie generowanej przez kompilator. Ponadto zakres zamknięcia jest zdefiniowany jako zakres deklaracji. Nie jest to tak naprawdę „jako referencje” jako takie („pass by referen” i „typy referencji” są dobrze zdefiniowane i żadne z nich tak naprawdę nie opisuje tego scenariusza)
Marc Gravell

5
@MarcGravell - masz rację. Powinienem tylko powiedzieć, że należy pamiętać, że jeśli nazwa pliku zmieni się przed rozpoczęciem wątku, zostanie użyta nowa wartość. Nie powinienem był bełkotać o jego mechanice i zdecydowanie nie powinienem mówić o odniesieniach.
tymtam

36

Spójrz na ten przykład:

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

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

Najpierw tworzysz wątek, przekazując delegata do metody pracownika, a następnie uruchamiasz go za pomocą metody Thread.Start, która przyjmuje obiekt jako parametr.

Więc w twoim przypadku powinieneś użyć go w ten sposób:

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

Ale twoja metoda „pobierania” nadal musi przyjmować obiekt , a nie ciąg znaków jako parametr. Możesz rzucić go na ciąg znaków w treści metody.


25

Chcesz użyć ParameterizedThreadStartdelegata do metod wątków, które pobierają parametry. (Lub w ogóle żaden, i pozwól Threadkonstruktorowi wnioskować.)

Przykładowe użycie:

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

thread.Start(filename)

7

Może ci się też delegatespodobać ...

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

4

W dodatkowej

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

3

Możesz obudować funkcję wątku (pobieranie) i potrzebne parametry (nazwę pliku) w klasie i użyć delegata ThreadStart do wykonania funkcji wątku.

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

Podobało mi się to podejście znacznie lepiej, stwierdziłem, że podejście do wyrażania lambda nie zawsze śledzi właściwe parametry
meanbunny

3

Poleciłbym ci inną klasę o nazwie File.

public class File
{
   private string filename;

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

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

W kodzie tworzenia wątków tworzysz nowy plik:

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);

0

Co powiesz na to: (czy można używać w ten sposób?)

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

-1

Zgodnie z twoim pytaniem ...

Jak przekazać parametry do metody Thread.ThreadStart () w C #?

... i napotkany błąd, musisz poprawić swój kod

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

do

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



Pytanie to jest jednak bardziej złożone, jak się wydaje na początku.

Obecnie Threadklasa (4.7.2) udostępnia kilka konstruktorów i Startmetodę z przeciążeniami.

Tymi odpowiednimi konstruktorami tego pytania są:

public Thread(ThreadStart start);

i

public Thread(ParameterizedThreadStart start);

które biorą ThreadStartdelegata lub ParameterizedThreadStartdelegata.

Odpowiedni delegaci wyglądają tak:

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

Jak można zauważyć, poprawnym konstruktorem do użycia wydaje się być ten, który bierze ParameterizedThreadStartdelegata, aby pewna metoda zgodna z określoną sygnaturą delegata mogła zostać uruchomiona przez wątek.

Prostym przykładem Threadbyłoby zainicjowanie klasy

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

Lub tylko

Thread thread = new Thread(Work);

Podpis odpowiedniej metody (wywołanej Workw tym przykładzie) wygląda następująco:

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

Pozostało rozpocząć wątek. Odbywa się to za pomocą jednego z nich

public void Start();

lub

public void Start(object parameter);

Chociaż Start()uruchamia wątek i przekazuje nulldane do metody, Start(...)może służyć do przekazywania wszystkiego do Workmetody wątku.

Jest jednak jeden duży problem z tym podejściem: wszystko przekazane do Workmetody jest rzutowane na obiekt. Oznacza to, że w ramach Workmetody należy ponownie rzutować na oryginalny typ, jak w poniższym przykładzie:

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



Przesyłanie jest czymś, czego zwykle nie chcesz robić.

Co się stanie, jeśli ktoś przekaże coś innego, co nie jest łańcuchem? Ponieważ na początku wydaje się to niemożliwe (ponieważ jest to moja metoda, wiem, co robię lub metoda ta jest prywatna, w jaki sposób ktoś powinien być w stanie przekazać coś do niej? ), Być może z tego powodu możesz skończyć dokładnie z tą sprawą . Ponieważ niektóre przypadki mogą nie stanowić problemu, inne tak. W takich przypadkach prawdopodobnie skończysz z InvalidCastExceptionczymś, czego prawdopodobnie nie zauważysz, ponieważ po prostu kończy wątek.

Jako rozwiązanie można oczekiwać ogólnego ParameterizedThreadStartdelegata, na przykład, ParameterizedThreadStart<T>gdzie Tbyłby typ danych, które chcesz przekazać do Workmetody. Niestety coś takiego jeszcze nie istnieje (jeszcze?).

Istnieje jednak sugerowane rozwiązanie tego problemu. Polega ona na utworzeniu klasy, która zawiera zarówno dane, które mają być przekazywane do wątku, jak i metodę reprezentującą metodę roboczą w następujący sposób:

public class ThreadWithState
{
    private string message;

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

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

Przy takim podejściu zaczynasz wątek w następujący sposób:

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

thread.Start();

W ten sposób po prostu unikniesz przerzucania się i masz bezpieczny sposób dostarczania danych do wątku ;-)


-2

oto idealny sposób ...

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);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.