BackgroundWorker i dostęp do Formy

0

pod eventem od przycisku odpala sie BW
pod BW.DoWork odpalam nowy form (w ktorym co 1s lecą dane na rs)
Problem polega na tym ze zatrzymuje BW po czym chciałbym odpalić go ponownie
I wtedy except cross threading przy dostępie do formy Start

        private void button2_Click(object sender, EventArgs e)
        {

            if (work == false)
            {
                try
                {
                  
                    button2.Text = "Stop";
                    backgroundWorker1.RunWorkerAsync();
                }


                catch
                {
                    MessageBox.Show("Nie można uruchomić wątku");
                }
            }
            else
            {
                backgroundWorker1.CancelAsync();

                button2.Text = "Start";
                work = false;
            }

           
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
      
               
                work = true;
                if (start == null) start = new Start(this); //To jest moj 2Form
                start.Show();
                if (backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

            }

            
            { }
        }

aha dodam ze forma zamyka się od środka (bardzo mozolnie zresztą)

0

byćmoże chodzi o start.show()
forma start pozostaje jeszcze z wcześniejszego bg (widocznie start != null).
I w ogóle zaprojektowanie jest jakieś dziwne, czemu start.show wykonuje się "ciągle"?

0

no i kibel na etapie projektowania!

  1. do czego BW - aby wykonac dlugotrwala operacje i nie blokowac watku glownego GUI
  2. wszystkie elementy graficzne (form, control) powinny byc tworzne w watku glownym GUI
  3. modyfikacje interfejsu (form, control) powinny odbywac sie w watku glownym GUI, w innym przypadku nalezy uzyc Invoke
  4. uzycie BW i uzywanie go jako timer? i po co ten dodatkowy form?

powinno byc tak:
jesli juz potrzebujesz dodatkowego okna, w ktorym bedziesz wyswietlal jakis wyniki pracy BW to utworz przed odpaleniem BW okno niemodalne
nastepnie odpal BW
jesli masz potrzebe z BW poinformawac okno o tym ze jakas porcja danych, komunikat moze byc wyswietlony, proponuje aby:
BW przekazac referencje do okna, a klasa okna bedzie udostepniala metody np. ShowMessage(string), ktora pobawi sie z InvokeRequired i poprawnie pokaze wiadomosc

jesli jednak potrzebujesz co jakis czas zrobic (cos odpytac i wyswietlic) zamiast BW uzyj timera, metoda do obslugi tick timera odpala sie w osobnym watku, wiec zasady sa te same co przy modyfikacji gui z BW

0

dzieki chyba macie racje
BW uzyje tylko to otwarcia rs i poslania danych zobacze jak to sie sprawdzi
i zeczywiscie start.show() w petli zrypalem / to przez ten upal
wielkie THX

0
massther napisał(a)

jesli masz potrzebe z BW poinformawac okno o tym ze jakas porcja danych, komunikat moze byc wyswietlony, proponuje aby:
BW przekazac referencje do okna, a klasa okna bedzie udostepniala metody np. ShowMessage(string), ktora pobawi sie z InvokeRequired i poprawnie pokaze wiadomosc

No to źle proponujesz. Klasę BackgroundWorker stworzono właśnie po to, żeby nie trzeba było się bawić w Invoke. Opisaną funkcjonalność można zrealizować za pomocą ReportProgress(wysłanie informacji) /OnProgressChanged (odebranie informacji w wątku gui).

Lektura:
http://msdn.microsoft.com/en-us/library/a3zbdb1t%28v=VS.100%29.aspx
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged%28v=VS.100%29.aspx

0

niby othello masz racje i mozna przez userState w ReportProgress przekazac jakies dane, tylko jesli dane ktore maja uaktualnic gui to nie jeden obiekt, to trzeba sobie zrobic jakis dodatkowe struktury zeby je enkapsulowac w jeden object, lub wszystko wpakowac to tablicy object[], czy innego brzydkiego tworu i w zdarzeniu pozniej wiedziec gdzie co jest etc.
oczywiscie jesli dane to jakis fragment listy, czy po prostu jakis string, czy inny dosc prosty obiekt, to nie ma problemu

0

Swoja droga, ja bym i tak tego nie robil tak jak zaproponowales, nawet gdybym mial nie uzywac ReportProgress. Lepiej by bylo chyba wyslac eventa do watku gui, czyli dopisac cos na wzor ReportProgress, ale z wlasnym EventArgs, do ktorego mozna wpakowac co sie chce.

0

Ale wtedy i tak trzeba będzie robić invoke, ponieważ event zajdzie w innym wątku a więc analogicznie będzie próbował modyfikować jakiś element naszego widoku z innego wątku.

Ile głów tyle pomysłów aczkolwiek sam wybrałbym chyba opcję eventa+invoke.
Pomiędzy warstwami modelu i widoku prosto było by to zrealizować

0

Tak, ale nie musi tak być - zależy jak to napiszesz. Zauważ, że OnProgressChanged nie wymaga Invoke. No ale to już inny temat.

0

poczatkujacemu nie chcialem mieszac jeszcze z eventami :)
ale macie racje, ja swoja droga tez takie rzeczy tak realizuje
ale warto zeby chlopak wiedzial o co chodzi z Invoke i dlaczego

0

Optowalbym za reportprogress/onprogresschanged. Nic prostszego/wygodniejszego sie nie wymysli.
A co do komunikacji cross-thread, i slynnego wyjatku, to fakt, warto, ale IMHO im pozniej, tym lepiej.
Jak sa prosciutkie frameworkowe metody by tego nie widziec, to po co samemu gonic tego królik :)

0

Fakt, onprogresschanged wystarczy w 99% przypadków. A jeżeli nie, to lepiej już bezpośrednio użyć wątków zamiast klasy BackgroundWorker, ale do takich prostych zadań to by było bez sensu (no chyba ze w celach edukacyjnych)

0

Jak mam wiecej zmiennych przezucac przez invoke to mnie czysci normalnie nie mogl ktos tego wymyslec ladniej/
Jeszcze 1 problem mam w clasie

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;

namespace AutoLemi
{
    class Gpsout
    {
        SerialPort g_port;
        Thread watekCOM;
        String odbior;
        bool _continue;
        delegate string SetValue(); 

        public Gpsout()
        { 
           
        }
        private string Wstaw()
        {
            return (string)Invoke(new SetValue(wstawianie));
        }

Error 1 The name 'Invoke' does not exist in the current context

0

Invoke to metoda z interfejsu ISynchronizeInvoke, ktory implementuje Control, a Form dziedziczy z Control, jak widze twoja klasa nie dziedziczy z Control czy czegos dziedziczacego z Control, ani nie implmentuje wymienionego interfejsu

p.s. ja tez ubolewam nad tym ze c# i VS jest glupie i jak mu mowie napisz super program to on nie chce :) ale startrek juz blisko :D

0

Napisz jak to teraz u ciebie wygląda, co robisz w oddzielnym wątku, a czego nie. Bo jakoś trudno wywnioskować jaki jest sosób działania tego programu.
PS. Invoke tylko przy pierwszym zetknięciu wydaje się dziwne i denerwujące, jak już to ogarniesz to zobaczysz że nie jest tak źle.

0

mam osobną klasę (juz dziedziczy po formie) ktora odbiera i prasuje protokol z gpsa (czyta co 1s)
zapisuje to co potrzebuje czyli wsp i ilosc satelitow w string[] i lapie to wszystko ladnie invokiem
czasami mam tylko problem przy zamykaniu calej aplikacji (bez recznego wylaczenia watku wczesniej)

1

mam osobną klasę (juz dziedziczy po formie)

8-O dziedziczysz po formie żeby używać metody invoke?

0
MSM napisał(a)

8-O dziedziczysz po formie żeby używać metody invoke?

Dziwisz się, jakbyś nigdy nie wbijał gwoździa wiertarką.

0

W tej chwili to dziedziczenie nie jest mi juz po nic potrzebne bo odwoluje sie do mojej klasy z watku glownego i z glownej formy
Ale skoro jest rozwiązanie lepsze niż wbijanie gwoździa wiertarką
to napiszcie więcej szczegółów przyda się na przyszłość
A kombinowalem z dziedziczeniem zgodnie z waszymi sugestiami ;p

0

No to jaki problem?Wszystko da sie zrobic na BackgroundWorker'ze. W DoWork robisz odczyt i przepakowanie tego wszystkiego co tam masz a w ReportProgress robisz update kontrolek(bez Invoke, bo juz nie trzeba).Mozesz jeszcze dodac WorkerComplete na zakonczenie operacji, ale u ciebie koniec dzialania to chyba zamkniecie aplikacji, wiec nie wiem czy to ci sie przyda. Nie mozesz tylko zapomniec ze trzeba ustawic dwie walsciwosci w BackgroundWorkerze - WorkerReportProgress i WorkerSupportCancellation na true. Ta ostatnia jest po to zebys nie zabijal brutalnie watku. W DoWork, w glownej petli musisz sprawdzac wlasciwosc CancellationPending BackgroundWorkera. Poszukaj w necie o tym na pewno znajdziesz(cancel BackgroundWorker).

0

Zgodnie z sugestią kolegów "Speców" odszedłem od BW zrobilem wszystko na zwykłym wątku
wszystko działa jak należy

        private string Wstaw()
        {
           return (string)Invoke(new SetValue(wstawianie));
        }
        private string wstawianie()
        {

            textwspx.Text = wspx;
            textwspy.Text = wspy;
            label1.Text = "Ilość Satelitów :" + sat;


            return textwspx.Text;
        }
     private void button4_Click(object sender, EventArgs e)
        {
            if (continueRun == false)
            {
                watekCOM = new Thread(new ThreadStart(odb));
                button4.Text = "GPS Stop";
                continueRun = true;
                if (gpsout == null)


                if (watekCOM.IsAlive)
                {
                    watekCOM.Join();
                }
                else
                {
                    watekCOM.Start();
                }
            }
            else
            {
                continueRun = false;
                button4.Text = "GPS Start";
            }
        }
   private void odb()
        {
            gpsout = new Gpsout();
            while (continueRun)
            {
                if (gpsout.wsp().Length >= 7)
                {
                    wspx = gpsout.wsp()[2];
                    wspy = gpsout.wsp()[4];
                    sat = gpsout.wsp()[7];
                    Wstaw();
                    Thread.Sleep(1000);
                }
            }
            if (watekCOM.IsAlive)
            {
                watekCOM.Abort();

            }
        }
0

no i ok. napisales w ten sposob 23% BgWrkera.. reszte jego dopiszesz,jesli okaze sie ze potrzebujesz komuniakcji z kontrolkami.. wszystko gra, najwyzej sobie wymienisz na bgw potem.

tylko.. po co Ci ten ostatni if+abort? jak ODB() sie skonczy, watek i tak zostanie zabity. Abort tylko niepotrzebnie exception podniesie, w dodaktu ---- w tym samym watku, co juz jest kompletnie bez sensu

0

Swoją drogą w .NET to trochę skomplikowali, bo w takim QT na przykład jeżeli sygnał jest wysłany z wątku działającego w tle, to slot wykonuje się już w wątku gui, więc bez obaw można uaktualniać kontrolki.

Osiągnięcie tego samego w .net nie jest już tak trywialne, wystarczy spojrzeć w kod BackgroundWorkera.

0

Prawda. To wynika z tego, ze eventy mimo uzywania delegatow, nie robia 'pelnego invoke' przez kolejki wiadomosci watkow, tylko od razu odpalaja docelowa metode.. No coz, trzeba przezyc. Albo napisac sobie delegatowrappera ktory bedzie auto-Control.Invoke-owal, czy tez Dispatcher... Az dziw mnie beirze, ze nawet niczego takiego jak dotad w systemowych dll nie znalazlem. upraszczanie sobie zycia jest az tak malo popularne?:)

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