¿Enviar argumentos al trabajador de fondo?

147

Digamos que quiero enviar un parámetro int a un trabajador en segundo plano, ¿cómo se puede lograr esto?

private void worker_DoWork(object sender, DoWorkEventArgs e) {

}

Sé cuándo se trata de worker.RunWorkerAsync () ;, no entiendo cómo definir en worker_DoWork que debería tomar un parámetro int.

sooprise
fuente

Respuestas:

235

Lo comienzas así:

int value = 123;
bgw1.RunWorkerAsync(argument: value);  // the int will be boxed

y entonces

private void worker_DoWork(object sender, DoWorkEventArgs e) 
{
   int value = (int) e.Argument;   // the 'argument' parameter resurfaces here

   ...

   // and to transport a result back to the main thread
   double result = 0.1 * value;
   e.Result = result;
}


// the Completed handler should follow this pattern 
// for Error and (optionally) Cancellation handling
private void worker_Completed(object sender, RunWorkerCompletedEventArgs e) 
{
  // check error, check cancel, then use result
  if (e.Error != null)
  {
     // handle the error
  }
  else if (e.Cancelled)
  {
     // handle cancellation
  }
  else
  {          
      double result = (double) e.Result;
      // use it on the UI thread
  }
  // general cleanup code, runs when there was an error or not.
}
Henk Holterman
fuente
38
¿Cómo puedo hacer dos argumentos?
sooprise
3
¿O envío un objeto lleno de más de un argumento?
sooprise
23
@soo: use una clase auxiliar o un Tuple<A,B>(C # 4 +) (Edición: Sí, use un objeto para empaquetarlo todo. Vea, por ejemplo, DoWorkEventArgs self).
Henk Holterman
Pero, ¿cómo notifica a la UI el resultado?
rayray
1
@rayray: en label1.Text = e.Result.ToString();todas partes marqué eso como seguro.
Henk Holterman
102

Aunque esta es una pregunta ya respondida, dejaría otra opción que IMO es mucho más fácil de leer:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (obj, e) => WorkerDoWork(value, text);
worker.RunWorkerAsync();

Y en el método del controlador:

private void WorkerDoWork(int value, string text) {
    ...
}
dcarneiro
fuente
12
No sabía qué significaba IMO, pensé que era una cosa de C #. Busqué en Google "C # IMO" y aterricé aquí y obtuve la respuesta ... lol quantnet.com/threads/cc-vba-or-java.11433
electricalbah
¿Qué tal 3 parámetros?
YukiSakura
No juego con .NET desde 2012, pero si no me equivoco, puede agregar los parámetros que desee ... => WorkerDoWork(a, b, c);siempre que coincida con la firma del método... WorkerDoWork(int a, string b, string c) {...
dcarneiro
1
Tenga en cuenta que si usó esto (como intenté hacer), debe crear un nuevo trabajador de fondo cada vez (en su ejemplo lo hizo). De lo contrario, tendrás un problema como yo. Mi trabajador de fondo seguiría repitiendo las ejecuciones anteriores. Si se ejecuta una vez, estaba bien. 2 veces repitió la última ejecución y la ejecución actual. La tercera carrera repetiría las dos últimas y la actual. etc.
bshea
Pero, ¿cómo se pasa el valor a RunWorkerAsync?
CodyBugstein
47

Puede pasar múltiples argumentos como este.

List<object> arguments = new List<object>();
                    arguments.Add(argument 1);
                    arguments.Add(argument 1);
                    arguments.Add(argument n);


                    backgroundWorker2.RunWorkerAsync(arguments);

private void worker_DoWork(object sender, DoWorkEventArgs e) {

  List<object> genericlist = e.Argument as List<object>;
  extract your multiple arguments from this list and cast them and use them. 

}
Zain Ali
fuente
@missReclusive emite los elementos de "lista genérica", es decir, digamos que "argumento 1" es de tipo int luego int argumento1 = (int) genericlist [0]
Zain Ali
1
Esta es una mala idea en términos de mantenimiento. Debería usar tipos concretos sobre List <object> porque al menos podrá darse cuenta de lo que estaba haciendo (vea un ejemplo en mi respuesta a continuación)
Denis
Probablemente preferiría una Tuple(o una clase especializada) en lugar de una lista de objetos genéricos
James S
6

Consulte la propiedad DoWorkEventArgs.Argument :

...
backgroundWorker1.RunWorkerAsync(yourInt);
...

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    // Do not access the form's BackgroundWorker reference directly.
    // Instead, use the reference provided by the sender parameter.
    BackgroundWorker bw = sender as BackgroundWorker;

    // Extract the argument.
    int arg = (int)e.Argument;

    // Start the time-consuming operation.
    e.Result = TimeConsumingOperation(bw, arg);

    // If the operation was canceled by the user, 
    // set the DoWorkEventArgs.Cancel property to true.
    if (bw.CancellationPending)
    {
        e.Cancel = true;
    }
}
Jay Riggs
fuente
5

puede probar esto si desea pasar más de un tipo de argumentos, primero agréguelos todos a una matriz de tipo Object y pase ese objeto a RunWorkerAsync () aquí hay un ejemplo:

   some_Method(){
   List<string> excludeList = new List<string>(); // list of strings
   string newPath ="some path";  // normal string
   Object[] args = {newPath,excludeList };
            backgroundAnalyzer.RunWorkerAsync(args);
      }

Ahora en el método doWork de trabajador de fondo

backgroundAnalyzer_DoWork(object sender, DoWorkEventArgs e)
      {
        backgroundAnalyzer.ReportProgress(50);
        Object[] arg = e.Argument as Object[];
        string path= (string)arg[0];
        List<string> lst = (List<string>) arg[1];
        .......
        // do something......
        //.....
       }
sujay
fuente
2
+1. Enviar los argumentos de esta manera también evita tener que iniciar un nuevo trabajador de fondo en cada ejecución para evitar repeticiones. (al menos en mi aplicación). Vea mi comentario a continuación relacionado con este problema. También stackoverflow.com/a/12231431/503621 y stackoverflow.com/questions/12507602/…
bshea
4

Siempre debe intentar usar un objeto compuesto con tipos concretos (usando un patrón de diseño compuesto) en lugar de una lista de tipos de objetos. ¿Quién recordaría qué diablos es cada uno de esos objetos? Piense en el mantenimiento de su código más adelante ... En su lugar, intente algo como esto:

Public (Class or Structure) MyPerson
                public string FirstName { get; set; }
                public string LastName { get; set; }
                public string Address { get; set; }
                public int ZipCode { get; set; }
End Class

Y entonces:

Dim person as new MyPerson With { .FirstName = Joe”,
                                  .LastName = "Smith”,
                                  ...
                                 }
backgroundWorker1.RunWorkerAsync(person)

y entonces:

private void backgroundWorker1_DoWork (object sender, DoWorkEventArgs e)
{
        MyPerson person = e.Argument as MyPerson
        string firstname = person.FirstName;
        string lastname = person.LastName;
        int zipcode = person.ZipCode;                                 
}
Denis
fuente