Enviando argumentos para o trabalhador em segundo plano?

147

Digamos que eu queira enviar um parâmetro int para um trabalhador em segundo plano, como isso pode ser feito?

private void worker_DoWork(object sender, DoWorkEventArgs e) {

}

Eu sei quando isso é worker.RunWorkerAsync () ;, eu não entendo como definir em worker_DoWork que ele deve usar um parâmetro int.

sooprise
fonte

Respostas:

235

Você começa assim:

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

e depois

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
fonte
38
Como posso fazer dois argumentos?
sooprise
3
Ou envio um objeto cheio de mais de um argumento?
sooprise
23
@soo: Use uma classe auxiliar ou um Tuple<A,B>(C # 4 +) (Editar: Sim, use um objeto para compactar tudo. Consulte, por exemplo, DoWorkEventArgs self).
Henk Holterman
Mas como você notifica a interface do usuário do resultado?
Rayray
1
@rayray:, em label1.Text = e.Result.ToString();todos os lugares eu marquei isso como seguro.
Henk Holterman
101

Mesmo que essa seja uma pergunta já respondida, deixaria outra opção que a IMO é muito mais fácil de ler:

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

E no método manipulador:

private void WorkerDoWork(int value, string text) {
    ...
}
dcarneiro
fonte
12
Eu não sabia o que IMO significava, eu pensei que era uma coisa C #. Eu pesquisei "C # IMO" e desembarcou aqui e tenho a resposta ... lol quantnet.com/threads/cc-vba-or-java.11433
electricalbah
Como cerca de 3 parâmetros?
YukiSakura
Eu não jogo com .NET desde 2012, mas se não me engano, você pode adicionar os parâmetros que você quer ... => WorkerDoWork(a, b, c);, contanto que corresponda a assinatura do método... WorkerDoWork(int a, string b, string c) {...
dcarneiro
1
Lembre-se de que, se você usou isso (como eu tentei fazer), é necessário criar um novo trabalhador em segundo plano a cada vez (no seu exemplo). Caso contrário, você terá um problema como eu. Meu técnico continuaria repetindo as execuções anteriores. Se executar uma vez estava tudo bem. 2 vezes repetiu a última corrida e a corrida atual. A terceira execução repetiria as duas últimas e a atual. etc.
bshea
Mas como o valor é passado para o RunWorkerAsync?
CodyBugstein
47

Você pode passar vários 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
fonte
@missReclusive lançar os itens "genericlist", ou seja, Vamos dizer "argumento 1" é do tipo int em seguida, int argument1 = (int) genericlist [0]
Zain Ali
1
essa é uma péssima idéia em termos de manutenção. Você deve usar tipos concretos sobre List <object>, porque pelo menos você vai ser capaz de descobrir o que você estava fazendo (veja um exemplo na minha resposta abaixo)
Denis
Eu provavelmente preferiria um Tuple(ou uma classe especializada) em vez de uma lista de objetos genéricos
James S
9

Você pode usar a DoWorkEventArgs.Argumentpropriedade

Um exemplo completo (mesmo usando um argumento int) pode ser encontrado no site da Microsoft:

Dave Mateer
fonte
6

Confira a propriedade 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
fonte
5

você pode tentar fazer isso se quiser passar mais de um tipo de argumento, primeiro adicione todos eles a uma matriz do tipo Object e passe esse objeto para RunWorkerAsync (). Aqui está um exemplo:

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

Agora no método doWork de trabalho em segundo plano

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
fonte
2
+1. O envio dos argumentos dessa maneira também evita a necessidade de iniciar um novo trabalhador em segundo plano a cada execução para evitar repetições. (pelo menos no meu aplicativo). Veja meu comentário abaixo relacionado a esse problema. Também stackoverflow.com/a/12231431/503621 & stackoverflow.com/questions/12507602/…
bshea
4

Você deve sempre tentar usar um objeto composto com tipos concretos (usando padrão de design composto) em vez de uma lista de tipos de objetos. Quem se lembraria do que diabos é cada um desses objetos? Pense na manutenção do seu código mais tarde ... Em vez disso, tente algo como isto:

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

E depois:

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

e depois:

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
fonte