¿Cómo pasar parámetros al método ThreadStart en Thread?

291

¿Cómo pasar parámetros al Thread.ThreadStart()método en C #?

Supongamos que tengo un método llamado 'descargar'

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

Ahora he creado un hilo en el método principal:

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

Tipo de método de error esperado.

¿Cómo puedo pasar parámetros a ThreadStartcon el método de destino con parámetros?

Swapnil Gupta
fuente
2
Echa un vistazo a este artículo escrito por Jon Skeet La sección Parámetros se encuentra en la página siguiente, pero el artículo en su conjunto es una buena lectura.
codingbadger

Respuestas:

696

Lo más simple es solo

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

La (s) ventaja (s) de esto (más ParameterizedThreadStart) es que puede pasar múltiples parámetros y obtener una verificación en tiempo de compilación sin necesidad de emitir objecttodo el tiempo.

Marc Gravell
fuente
15
Perdón por el tema, pero ¿qué significa el operador '()'? A veces lo veo pero no tengo tiempo para comprobarlo.
ŁukaszW.pl
24
Es una expresión lambda sin argumentos.
Noldorin
31
@ ŁukaszW.pl - lo que dijo Noldorin; p en C # 2.0 una construcción alternativa (para este ejemplo) esnew Thread(delegate() { download(filename); });
Marc Gravell
77
@Tymek eso no es del todo exacto; las variables capturadas se tratan como cierres léxicos completos , que (como detalle de implementación) se implementan como campos en una clase generada por el compilador. Además, el alcance del cierre se define como el alcance de la declaración. No es realmente "como referencias" como tales ("pasar por referencia" y "tipos de referencia" están bien definidos, y ninguno describe realmente este escenario)
Marc Gravell
55
@MarcGravell: tienes razón. Todo lo que debería haber dicho es que uno debe tener en cuenta que si el 'nombre de archivo' cambia antes de que comience el hilo, se usará el nuevo valor. No debería haber estado parloteando sobre su mecánica y definitivamente no debería estar hablando de referencias.
tymtam
36

Mira este ejemplo:

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

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

Primero está creando un hilo pasando el método delegado al trabajador y luego lo inicia con un método Thread.Start que toma su objeto como parámetro.

Entonces, en su caso, debe usarlo así:

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

Pero su método de 'descarga' todavía necesita tomar un objeto , no una cadena como parámetro. Puedes convertirlo en cadena en el cuerpo de tu método.

ŁukaszW.pl
fuente
25

Desea utilizar el ParameterizedThreadStartdelegado para métodos de subprocesos que toman parámetros. (O ninguno en realidad, y dejar que el Threadconstructor infiera).

Ejemplo de uso:

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

thread.Start(filename)
Noldorin
fuente
7

También te podría delegategustar así ...

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

En adicional

    Thread thread = new Thread(delegate() { download(i); });
    thread.Start();
Metin Atalay
fuente
3

Puede encapsular la función de subproceso (descarga) y los parámetros necesarios (nombre de archivo) en una clase y utilizar el delegado ThreadStart para ejecutar la función de subproceso.

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
fuente
Me gusta mucho este enfoque, descubrí que el enfoque de la expresión lambda no siempre hace un seguimiento de los parámetros correctos
meanbunny
3

Te recomendaría tener otra clase llamada File.

public class File
{
   private string filename;

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

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

Y en su código de creación de hilo, crea una instancia de un nuevo archivo:

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);
João Pedro Andrade Marques
fuente
0

Qué tal esto: (¿o está bien usarlo así?)

var test = "Hello";
new Thread(new ThreadStart(() =>
{
    try
    {
        //Staff to do
        Console.WriteLine(test);
    }
    catch (Exception ex)
    {
        throw;
    }
})).Start();
Cansın Şenalioğlu
fuente
-1

De acuerdo a tu pregunta ...

¿Cómo pasar parámetros al método Thread.ThreadStart () en C #?

... y el error que encontró, tendría que corregir su código de

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

a

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



Sin embargo, la pregunta es más compleja como parece al principio.

La Threadclase actualmente (4.7.2) proporciona varios constructores y un Startmétodo con sobrecargas.

Estos constructores relevantes para esta pregunta son:

public Thread(ThreadStart start);

y

public Thread(ParameterizedThreadStart start);

que o bien toman un ThreadStartdelegado o un ParameterizedThreadStartdelegado.

Los delegados correspondientes se ven así:

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

Como se puede ver, el constructor correcto para usar parece ser el que toma un ParameterizedThreadStartdelegado para que el hilo pueda iniciar algún método conforme a la firma especificada del delegado.

Un ejemplo simple para instanciar la Threadclase sería

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

o solo

Thread thread = new Thread(Work);

La firma del método correspondiente (llamado Worken este ejemplo) se ve así:

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

Lo que queda es comenzar el hilo. Esto se hace usando cualquiera

public void Start();

o

public void Start(object parameter);

Si bien Start()iniciaría el hilo y pasaría nullcomo datos al método, Start(...)se puede usar para pasar cualquier cosa al Workmétodo del hilo.

Sin embargo, hay un gran problema con este enfoque: todo lo que se pasa al Workmétodo se convierte en un objeto. Eso significa que dentro del Workmétodo tiene que volver al tipo original, como en el siguiente ejemplo:

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



El casting es algo que normalmente no quieres hacer.

¿Qué pasa si alguien pasa algo más que no es una cadena? Como parece que esto no es posible al principio (porque es mi método, sé lo que hago o el método es privado, ¿cómo debería alguien ser capaz de pasarle algo? ) Es posible que termine exactamente con ese caso por varias razones . Como algunos casos pueden no ser un problema, otros sí lo son. En tales casos, probablemente terminará con uno InvalidCastExceptionque probablemente no notará porque simplemente termina el hilo.

Como solución, esperaría obtener un ParameterizedThreadStartdelegado genérico como ParameterizedThreadStart<T>dónde Tsería el tipo de datos que desea pasar al Workmétodo. Lamentablemente, algo como esto no existe (¿todavía?).

Sin embargo, hay una solución sugerida para este problema. Implica crear una clase que contenga ambos, los datos que se pasarán al subproceso y el método que representa el método de trabajo de esta manera:

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 este enfoque, comenzaría el hilo de esta manera:

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

thread.Start();

De esta manera, simplemente evitas dar vueltas y tienes una forma segura de proporcionar datos a un hilo ;-)

Markus Safar
fuente
-2

aquí está la manera perfecta ...

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
fuente