Wysyłasz argumenty do pracownika w tle?

147

Powiedzmy, że chcę wysłać parametr int do pracownika w tle, jak można to osiągnąć?

private void worker_DoWork(object sender, DoWorkEventArgs e) {

}

Wiem, kiedy to jest worker.RunWorkerAsync ();, nie rozumiem, jak zdefiniować w pliku worker_DoWork, że powinien przyjmować parametr int.

sooprise
źródło

Odpowiedzi:

235

Zaczynasz tak:

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

i wtedy

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
źródło
38
Jak mogę przedstawić dwa argumenty?
Sooprise
3
A może mam wysłać obiekt zawierający więcej niż jeden argument?
sooprise
23
@soo: Użyj klasy pomocniczej lub Tuple<A,B>(C # 4 +) (Edycja: Tak, użyj obiektu, aby spakować wszystko. Zobacz na przykład DoWorkEventArgs self).
Henk Holterman
Ale jak powiadomić interfejs użytkownika o wyniku?
rayray
1
@rayray:, label1.Text = e.Result.ToString();wszędzie oznaczyłem to jako bezpieczne.
Henk Holterman
101

Mimo że jest to już odpowiedź na pytanie, zostawiłbym inną opcję, że IMO jest o wiele łatwiejsze do odczytania:

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

A na metodzie obsługi:

private void WorkerDoWork(int value, string text) {
    ...
}
dcarneiro
źródło
12
Nie wiedziałem, co oznacza IMO, myślałem, że to rzecz C #. Wyszukałem w Google „C # IMO” i wylądowałem tutaj i otrzymałem odpowiedź ... lol quantnet.com/threads/cc-vba-or-java.11433
electricbah
A co z 3 parametrami?
YukiSakura
Nie gram z .NET od 2012 roku, ale jeśli się nie mylę, możesz dodać ... => WorkerDoWork(a, b, c);... WorkerDoWork(int a, string b, string c) {...
żądane
1
Pamiętaj, że jeśli użyłeś tego (tak jak próbowałem to zrobić), za każdym razem musisz utworzyć nowego pracownika w tle (w swoim przykładzie zrobiłeś). W przeciwnym razie będziesz miał problem, tak jak ja. Mój pracownik w tle będzie powtarzał poprzednie uruchomienia. Jeśli raz uruchomisz, było dobrze. 2 razy powtórzyło ostatni bieg i bieżący bieg. Trzeci bieg będzie powtarzał ostatnie dwa i aktualne. itd.
bshea
Ale w jaki sposób wartość jest przekazywana do RunWorkerAsync?
CodyBugstein
47

Możesz przekazać wiele takich argumentów.

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
źródło
@missReclusive rzutuje elementy "genericlist", tj. Powiedzmy, że "argument 1" jest typu int then int argument1 = (int) genericlist [0]
Zain Ali
1
to zły pomysł z punktu widzenia konserwacji. Powinieneś używać konkretnych typów zamiast List <object>, ponieważ przynajmniej będziesz w stanie dowiedzieć się, co robiłeś (zobacz przykład w mojej odpowiedzi poniżej)
Denis
Prawdopodobnie wolałbym Tuple(lub wyspecjalizowaną klasę) zamiast listy ogólnych obiektów
James S
9

Możesz korzystać z DoWorkEventArgs.Argumentnieruchomości.

Pełny przykład (nawet używając argumentu int) można znaleźć na stronie Microsoftu:

Dave Mateer
źródło
6

Sprawdź właściwość 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
źródło
5

możesz to wypróbować, jeśli chcesz przekazać więcej niż jeden typ argumentów, najpierw dodaj je wszystkie do tablicy typu Object i przekaż ten obiekt do RunWorkerAsync (). Oto przykład:

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

Teraz w metodzie doWork pracownika w tle

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
źródło
2
+1. Wysyłanie argumentów w ten sposób pozwala również uniknąć konieczności uruchamiania nowego pracownika w tle przy każdym przebiegu, aby uniknąć powtórzeń. (przynajmniej w mojej aplikacji). Zobacz mój komentarz poniżej dotyczący tego problemu. Również stackoverflow.com/a/12231431/503621 & stackoverflow.com/questions/12507602/ ...
bshea
4

Należy zawsze starać się używać obiektu złożonego z typami betonowymi (przy użyciu wzorca projektowego złożonego) zamiast listy typów obiektów. Kto by pamiętał, czym do cholery jest każdy z tych obiektów? Pomyśl o późniejszej konserwacji swojego kodu ... Zamiast tego spróbuj czegoś takiego:

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

I wtedy:

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

i wtedy:

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
źródło