Come passare i parametri al metodo ThreadStart in Thread?


291

Come passare i parametri al Thread.ThreadStart()metodo in C #?

Supponiamo di avere un metodo chiamato 'download'

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

Ora ho creato un thread nel metodo principale:

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

tipo di metodo di errore previsto.

Come posso passare parametri ThreadStartcon il metodo target con parametri?


2
Dai un'occhiata a questo articolo scritto da Jon Skeet La sezione Parametri è nella pagina successiva ma l'articolo nel suo insieme è una lettura abbastanza buona.
codingbadger,

Risposte:


696

Il più semplice è giusto

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

Il (i) vantaggio (i) di questo (sopra ParameterizedThreadStart) è che puoi passare più parametri e ottenere il controllo in fase di compilazione senza dover eseguire il cast da objecttutto il tempo.


15
Mi dispiace per l'offtopico ma cosa significa l'operatore '()'? Lo vedo a volte ma non ho tempo di controllare.
ŁukaszW.pl,

24
È un'espressione lambda senza argomenti.
Noldorin,

31
@ ŁukaszW.pl - cosa ha detto Noldorin; p in C # 2.0 un costrutto alternativo (per questo esempio) ènew Thread(delegate() { download(filename); });
Marc Gravell

7
@Tymek non è abbastanza preciso; tutte le variabili acquisite vengono trattate come chiusure lessicali complete , che (come dettaglio di implementazione) vengono implementate come campi in una classe generata dal compilatore. Inoltre, l'ambito di chiusura è definito come ambito della dichiarazione. In realtà non è "come riferimento" in quanto tale ("passaggio per riferimento" e "tipi di riferimento" sono entrambi ben definiti e non descrivono realmente questo scenario)
Marc Gravell

5
@MarcGravell - hai ragione. Tutto quello che avrei dovuto dire è che bisogna essere consapevoli che se il 'nome file' cambia prima dell'inizio del thread, verrà usato il nuovo valore. Non avrei dovuto blaterare della meccanica di ciò e sicuramente non avrei dovuto parlare di riferimenti.
Tymtam,

36

Guarda questo esempio:

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

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

Innanzitutto si crea un thread passando delegato al metodo worker e quindi lo si avvia con un metodo Thread.Start che accetta l'oggetto come parametro.

Quindi nel tuo caso dovresti usarlo in questo modo:

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

Ma il tuo metodo di "download" deve ancora prendere l' oggetto , non la stringa come parametro. Puoi lanciarlo su stringa nel corpo del tuo metodo.


25

Si desidera utilizzare il ParameterizedThreadStartdelegato per i metodi di thread che accettano parametri. (O nessuno affatto in realtà, e lasciare che il Threadcostruttore deduca.)

Esempio di utilizzo:

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

thread.Start(filename)

7

Potrebbe delegatepiacerti anche così ...

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

4

In aggiuntivo

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

3

È possibile incapsulare la funzione thread (download) e i parametri necessari (nome file) in una classe e utilizzare il delegato ThreadStart per eseguire la funzione thread.

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

Mi piace molto meglio questo approccio, ho scoperto che l'approccio lambda non tiene sempre traccia dei parametri giusti
meanbunny

3

Ti consiglierei di avere un'altra classe chiamata File.

public class File
{
   private string filename;

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

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

E nel tuo codice di creazione del thread, crei un'istanza di un nuovo file:

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);

0

Che ne dici di questo: (o va bene usare così?)

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

-1

Secondo la tua domanda ...

Come passare i parametri al metodo Thread.ThreadStart () in C #?

... e l'errore che hai riscontrato, dovresti correggere il tuo codice

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

per

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



Tuttavia, la domanda è più complessa come sembra all'inizio.

La Threadclasse attualmente (4.7.2) fornisce diversi costruttori e un Startmetodo con sovraccarichi.

Questi costruttori rilevanti per questa domanda sono:

public Thread(ThreadStart start);

e

public Thread(ParameterizedThreadStart start);

che può assumere un ThreadStartdelegato o un ParameterizedThreadStartdelegato.

I delegati corrispondenti si presentano così:

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

Come si può vedere, il costruttore corretto da usare sembra essere quello che prende un ParameterizedThreadStartdelegato in modo che un thread conforme alla firma specificata del delegato possa essere avviato dal thread.

Un semplice esempio per istanziare la Threadclasse sarebbe

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

o solo

Thread thread = new Thread(Work);

La firma del metodo corrispondente (chiamato Workin questo esempio) è simile alla seguente:

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

Ciò che resta è iniziare il thread. Questo viene fatto usando uno dei due

public void Start();

o

public void Start(object parameter);

Mentre Start()avviare il thread e passare nullcome dati al metodo, Start(...)può essere utilizzato per passare qualsiasi cosa nel Workmetodo del thread.

Esiste tuttavia un grosso problema con questo approccio: tutto ciò che viene passato nel Workmetodo viene trasmesso in un oggetto. Ciò significa che all'interno del Workmetodo deve essere nuovamente eseguito il cast nel tipo originale come nell'esempio seguente:

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



Il casting è qualcosa che in genere non si desidera fare.

Cosa succede se qualcuno passa qualcos'altro che non è una stringa? Dal momento che questo non sembra possibile all'inizio (poiché è il mio metodo, so cosa faccio o Il metodo è privato, come dovrebbe mai qualcuno essere in grado di passargli qualcosa? ), Potresti finire con quel caso per vari motivi . Poiché alcuni casi potrebbero non essere un problema, altri lo sono. In questi casi probabilmente finirai con unInvalidCastException che probabilmente non noterai perché termina semplicemente il thread.

Come soluzione ti aspetteresti di ottenere un ParameterizedThreadStartdelegato generico come ParameterizedThreadStart<T>dove Tsarebbe il tipo di dati che vuoi passare al Workmetodo. Purtroppo qualcosa del genere non esiste (ancora?).

Esiste tuttavia una soluzione suggerita a questo problema. Implica la creazione di una classe che contenga sia i dati da passare al thread sia il metodo che rappresenta il metodo worker in questo modo:

public class ThreadWithState
{
    private string message;

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

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

Con questo approccio inizieresti il ​​thread in questo modo:

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

thread.Start();

Quindi in questo modo eviti semplicemente di lanciarti e hai un modo semplice di fornire dati a un thread ;-)


-2

ecco il modo perfetto ...

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.