Dziwne wyniki, BackgroundWorker

Odpowiedz Nowy wątek
2014-12-13 14:58
0

Witam,
próbuję robić proste losowanie liczby korzystając z BackgroundWorker, jednak dzieją się dziwne rzeczy, a rozwiązania dojść nie mogę.
Dla testów, losuje 10 liczb z przedziału -1000 do 1000. Wygląd mojego kodu:

 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
 
                ilosc_liczb = Convert.ToInt32(textBox2.Text);
                tablica = new int [ilosc_liczb]; 
                przedzialOd = Convert.ToInt32(numericUpDown1.Value); 
                przedzialDo = Convert.ToInt32(numericUpDown2.Value); 
                Random random = new Random(); 
 
                double ilosc_liczb_double = Convert.ToDouble(ilosc_liczb); 
                ile = 100 / ilosc_liczb_double; 
 
                for (i = 0; i < ilosc_liczb; i++) 
                {
                    if (worker.CancellationPending == true) 
                    {
                        e.Cancel = true; 
                        break; 
                    }
                    else 
                    {
                        liczba = random.Next(przedzialOd, przedzialDo); 
                        tablica[i] = liczba; 
                        liczba_w_string = liczba.ToString(); 
 
                        procent = procent + ile; 
                        System.Threading.Thread.Sleep(5); 
                        worker.ReportProgress(Convert.ToInt32(procent)); 
                    }
                }
        }
 
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            textBox1.AppendText("Liczba nr " + i + ": " + liczba_w_string + "\n");
        }

Zmienna "i" sterująca pętlą odpowiada za wpis do tablicy na pozycji i 0-9 (więc 10 liczb) oraz wypisywanie liczb. Teoretycznie, powinno wypisywać zawsze od Liczba nr 0: do Liczba nr 9:
Jak zauważycie na screenach poniżej, jest inaczej. Dlaczego?
Podczas debugowania każdej iteracji wyniki zawsze zwracane są dobre, inaczej sprawa się ma gdy puszcze aplikację bez opcji debugowania, wtedy wypisuje od 1 do 10 (zamiast 0 do 9) oraz niekiedy powiela niektóre iteracje pomijając inne. Co może być tego przyczyną?
Załączam screeny pomocnicze:
fc0kab68bbvl_t.jpgzer5yg7r9r29_t.jpgzlwnmqo1yw5r_t.jpgetn2pv4e9j8i_t.jpg
mkwkyrm6kh8q_t.jpgy6cif16v2zk7_t.jpg

Jak widać dla przypadków gdy robiłem debugging wyniki wyświetliły się dobrze, beż żadnych powtórzeń czy liczb nr. 10, których imo być nie powinno, skoro for przechodzi od 0 do 9.

Pozostało 580 znaków

2014-12-13 15:08
0

Nie używałem tego nigdy, ale prawdopodobnie ProgressChanged nie wykonuje się od razu tylko czeka aż główny wątek mu "pozwoli" się wykonać. Tak jak by się wykonało metodę przez Invoke.

Pozostało 580 znaków

2014-12-13 15:19
0

Rozumiem w takim razie, że nie jest to zbyt dobre rozwiązanie. W takim razie proszę o ukierunkowanie mnie, jak pracować, aby móc przerwać działanie pętli przyciskiem, nie używając barckgroundworker. Głównie na tym mi zależało, na możliwości przerwania operacji, a podczas pracy na głównym wątku akcja (przyciśnięcie innego przycisku) się nie wykona dopóki pętla się nie zakończy.

Pozostało 580 znaków

2014-12-13 15:27
0

semafory, mutexy, eventy?


Pozostało 580 znaków

2014-12-13 15:42
0

worker.ReportProgress(Convert.ToInt32(procent)); Przekaż tu potrzebna argumenty, niby coś tam przekazujesz ale tego nie wykorzystujesz tylko pobierasz z wartości z pól które mogły się przez ten czas zmienić.

Pozostało 580 znaków

2014-12-13 19:51
0

Według dokumentacji:
http://msdn.microsoft.com/pl-[...]y/ka89zff4%28v=vs.110%29.aspx

The call to the ReportProgress method is asynchronous and returns immediately. The ProgressChanged event handler executes on the thread that created the BackgroundWorker.

Więc tak naprawdę nie masz gwarancji czy po wywołaniu jesteś jeszcze na i=0 czy pętla w BackgroundWorkerze przeskoczyła juz na kolejną wartość i. Thread.Sleep przed ReportProgress raczej nie pomoże, prędzej za.

Żeby temu zaradzić spróbuj użyć

public void ReportProgress(int percentProgress, Object userState)

i jako userState przekazywać np klasę z aktualną wartością i oraz liczba_w_string, wtedy będziesz mieć pewność, że w funkcji masz takie wartości jakie były w momencie wywołania.

Pozostało 580 znaków

2014-12-14 17:33
0

Dzięki za porady. W zasadzie na razie testuje "programik" po przeniesieniu Thread.Sleep za ReportProgress i wyniki są obiecujące. Fakt, czasem zdarzają się cuda np. pomijanie jakiejś iteracji lub wypisanie ostatniej wielokrotnie, ale radzi sobie z tym zwiększony sleep o 1. Być może nie jest to dobre, ale na razie mi wystarcza.

Pozostało 580 znaków

2014-12-14 18:01

Dostałeś podpowiedzi i mimo to nie potrafisz z nich skorzystać tylko tworzysz jakieś wymysły.

Tworzysz klasę z właściwościami które chcesz przekazać do progressChanged

class MojaKlasa
{
   public int TwojeI {get;set;}
   public string TwojaLiczba {get;set;}
}

Wywołujesz reportProgress z twoimi zmiennymi;

worker.ReportProgress(0,new MojaKlasa(){TwojeI = i, TwojaLiczba = liczba); //na początku jest 0 bo trzeba podać jakiś argument a tobie on raczej nie jest potrzebny, więc dajemy cokolwiek

I tak dostajesz się do nich tak:

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var mojaKlasa = e.UserState as MojaKlasa;
            textBox1.AppendText("Liczba nr " + mojaKlasa.TwojeI + ": " + mojaKlasa.TwojaLiczba + "\n");
        }

Pozostało 580 znaków

2014-12-14 20:00
0

Dziękuję @dam1an za wyjaśnienie sprawy. Do pierwszego parametru ReportProgress podałem sobie obecny stan procentowy, dzięki czemu wyświetlam aktualny progressBar wątku.

worker.ReportProgress(Convert.ToInt32(procent), new MojaKlasa() { TwojeI = i, TwojaLiczba = liczba_w_string }); 

A samo backgroundWorker1_ProgressChanged:

       private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var mojaKlasa = e.UserState as MojaKlasa;
            progressBar1.Value = e.ProgressPercentage; 
            textBox1.AppendText("Liczba nr " + (i+1) + ": " + liczba_w_string + "\n"); 
        }
 

Dzięki temu mam chyba wszystko czego potrzebowałem, bez pominięć czy powtórzeń + działający progressBar. Dziękuję.

poza i+1 niewiele się chyba zmieniło ;P - Wielki Szczur 2014-12-14 20:28
nie no doszedł parametr dla reportprogress i update progressbara :) - peterkovic 2014-12-14 20:31
Jeszcze raz przyjrzyj się mojemu przykładowi, po co Ci var mojaKlasa = e.UserState as MojaKlasa; jak nigdzie tego nie wykorzystujesz? Masz wciąż ten sam kod co na początku i on ciągle będzie działał źle. - dam1an 2014-12-14 21:14
No tak, nie trzeba być widomym żeby widzieć że tego nie zbyt widocznie rozumiem. Dodałem klasę, dodałem parametry odpowiednio w reportprogress. O dziwo, program wydaje się funkcjonować dobrze. Skoro nie, to czy będziesz tak cierpliwy aby wytłumaczyć to jeszcze raz? - peterkovic 2014-12-14 21:56

Pozostało 580 znaków

2014-12-14 22:03
1

No tak, nie trzeba być widomym żeby widzieć że tego nie zbyt widocznie rozumiem. Dodałem klasę, dodałem parametry odpowiednio w reportprogress. O dziwo, program wydaje się funkcjonować dobrze. Skoro nie, to czy będziesz tak cierpliwy aby wytłumaczyć to jeszcze raz? -

Ja nie wiem co można tu tłumaczyć musisz tylko przekopiować tą linijkę:

 textBox1.AppendText("Liczba nr " + mojaKlasa.TwojeI + ": " + mojaKlasa.TwojaLiczba + "\n");

Zalecałbym też pozmieniać nazwy MojaKlasa, TwojeI itp, na jakieś bardziej odpowiadające temu czym są, nazwałem to tak bo nie chce mi się dociekać co ty tam liczysz.
Ale to może jak już uda Ci się poprawnie przekopiować i zrozumieć co tu się dzieje.

Nie wiem jak mogłem to przeoczyć... dzięki - peterkovic 2014-12-15 16:54

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0