2015-08-22 14 views
5

Potrzebuję wygenerować n losowych ciągów, a proces ten może chwilę potrwać i zablokować interfejs głównego wątku. Aby tego uniknąć i pozwolić użytkownikowi korzystać z programu podczas jego działania, postanowiłem użyć backGroundWorker. Ale nie działało dobrze, a główny wątek nadal jest zablokowany. W moim DoWork razie mam coś takiego:Kod uruchomienia bez głównego wątku bloku

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
     { 
      // using Invoke because I change value of some controls, e.g, ListView, labels and a progressbar 
      this.Invoke((Action)delegate 
      { 
       for (int i = 0; i < nSteps; ++i) 
       { 
        string s = getnStr(); 
        // ... 
        int progress = (int)(100.0/nSteps * (i + 1)); 
        backgroundWorker1.ReportProgress(progress); 
       } 
      }); 
     } 

Nawet chociaż ja nazywam ReportProgress() wewnątrz pętli progressbar tylko zmienia swoją wartość, gdy pętla jest wykonywana.

Dlaczego tak się dzieje i jak mogę to naprawić?

+0

Nie trzeba wywoływać, jeśli aktualizacja odbywa się w module obsługi zdarzeń Postęp. – alexm

+0

I również etykiety pętli wewnątrz pętli. – Jack

+2

Możesz zmienić etykiety za pomocą ReportProgress, a także podpowiedź: ma UserData – alexm

Odpowiedz

9

Wywołaj ... To tam wszystko źle się dzieje. Tworzysz tylko robot działający w tle tylko po to, aby wywołać kod z powrotem do głównego wątku (to właśnie wywołuje Invoke).

Powinieneś użyć wywołania tylko w celu uzyskania dostępu do kontrolek, albo zmodyfikuj formanty tylko w procedurze obsługi ReportProgress, a następnie upewnij się, że wywołujesz metodę ReportProgress za każdym razem, gdy jest to potrzebne.

EDYCJA: Wyjaśnienie: Twój problem z wywołaniem polega na tym, że wywołujesz wątek interfejsu użytkownika dla całego obciążenia, które miał wykonywać robotnik w tle.

3

Inna odpowiedź wyjaśniła zachowanie, które widzisz. Przeczytaj to najpierw. Oto, jak to naprawić:

BackgroundWorker jest przestarzały, ponieważ mamy teraz Task i await teraz. Bardzo dużo automatyzuje. Twój kod prawdopodobnie powinien wyglądać tak:

  //In the button click handler: 
      for (int i = 0; i < nSteps; ++i) 
      { 
       await DoSomeWorkAsync(); 
       int progress = (int)(100.0/nSteps * (i + 1)); 
       SetProgressBarValue(progress); 
      } 

To wszystko. Musisz się upewnić, że DoSomeWorkAsync nie blokuje. Musi zwrócić wartość Task.

1

Poza raportowanie postępu ReportProgress() metoda może być stosowana jako ogólny asynchronicznego dyspozytora zdarzeń (np zaktualizować tekst w kontroli UI):

for (int i = 0; i < nSteps; ++i) 
{ 
    string s = getnStr(); 

    // Update text 
    backgroundWorker1.ReportProgress(0, "My text..");  

    // Update progress 
    int progress = (int)(100.0/nSteps * (i + 1)); 
    backgroundWorker1.ReportProgress(progress); 
} 

ProgressChanged obsługi zdarzeń będzie wyglądać mniej więcej tak:

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    if (e.UserState != null) 
    { 
      // Handle text update 
      label1.Text = (string)e.UserState; 
      return; 
    } 

    progressBar1.Value = e.ProgressPercentage; 
} 
+0

Myśli, co zrobiłem w moim programie obsługi zdarzeń 'ProgressChanged' =), dzięki czemu mogę dodawać elementy do listy viewView, zmieniać labelText lub progressBar. Użyłem operatora 'is' do określenia, co zrobić. – Jack

+0

Wywołanie' RTB.AppendText() 'z' ProgressChanged' powoduje blokowanie wątku w ten sam sposób. Wszelkie sugestie, jak mogę rozwiązać ten problem? – Jack

Powiązane problemy