CYFROWY BARON • PROGRAMOWANIE • Zobacz wątek - delegate raz jeszcze...

delegate raz jeszcze...

dział ogólny

delegate raz jeszcze...

Nowy postprzez Slynx » środa, 29 czerwca 2011, 11:03

Muszę wrócić do problemu, bo się trochę pogubiłem w funkcji do downloadu. Kodu jest dużo, to postaram się raczej opisać i dać tylko kawałki.
Jest osobna klasa do downloadu, która wewnątrz odpala pobieranie na nowym wątku. Jedyną informacją, że "żyje", jest ciągłe invoke (nasz delegate) z informacją o ilości pobranych danych. Wcześniej przez argument podawałem całego progressbara, którego aktualizował, ale teraz w nowej wersji komponentu, który ma obsługiwać, progressbar jako taki nie istnieje. Zapis odbywa się poprzez specjalną funkcję, która aktualizuje cały rekord, za pomocą danych typu String^ lub int.

To jest typowe pobieranie.
KOD cpp:     UKRYJ  
 Download^ down = gcnew Download("http://programowanie.cal.pl/babilon/babilonsetup203.zip","C://babilon.zip", false, label3->Text);
 down->Create_New_Thread();
 

Co do klasy Download (.h)
KOD cpp:     UKRYJ  
String^ progress;
Void Update_Progress(int percentage_progress);
delegate System::Void UpdateProgessCallback(int percentage_progress);
UpdateProgessCallback ^upd;
Download(String^ url, String^ download_file, bool paused, String^ out);
 

Plik cpp
KOD cpp:     UKRYJ  
                Download::Download(String^ url, String^ download_file, bool paused, String^ out)
                {
                        url_ = url;
                        local_file_path_ = download_file;
                        progress = &out;
                        upd = gcnew UpdateProgessCallback(this, &Download::Update_Progress);
                };
                Void Download::Update_Progress(int percentage_progress)
                {
                        progress = percentage_progress.ToString();
                }
//oraz w pętli już samego pobierania
     upd->Invoke(stream_local->Length);
/// gdzie stream_local zawiera pobrane dane
 

To chyba wszystko co jest potrzebne.
Więc, można takiego Stringa jakoś podać przez konstruktor by go aktualizował. Mam jeszcze jedno pytanie, bo już nie jestem pewien co do tego Invoke.
KOD cpp:     UKRYJ  
String^ text = label3->Text;
Download^ down = gcnew Download("http://programowanie.cal.pl/babilon/babilonsetup203.zip","C://babilon.zip", false, text);
 down->Create_New_Thread();
 

Czy to też by działało ? Przypominam, że "String^ text = label3->Text;" wykonałoby się tylko raz, bo nie jest w pętli.
Próbowałem jeszcze dodać do tego Stringa jakieś referencje(czyli tak jakbym zrobił w natywnym), ale trochę mi namieszaliście faktem, że "^" też oznacza referencje, to już się pogubiłem czy zapis tego typu "String^ &tekst - label3->Text;" miałby jakikolwiek sens:/
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: delegate raz jeszcze...

Nowy postprzez polymorphism » środa, 29 czerwca 2011, 11:58

(...) ale trochę mi namieszaliście faktem, że "^" też oznacza referencje

Że co?! Pokaż mi miejsce, gdzie twierdze, że ^ to referencja. To jest uchwyt do pamięci zarządzanej. To po pierwsze. Po drugie, referencja dla pamięci zarządzanej to %, a nie &.

KOD cpp:     UKRYJ  
Download::Download(..., String^ out)
{
        ...
        progress = &out;
}

Abstrahując od poprawności składniowej, przypisujesz adres parametru out, a Tobie chodzi o adres/referencję zmiennej z zewnątrz. out i progress muszą być referencją, ponieważ obiekty klasy String są obiektami stałymi (immutable objects), zatem przypisując w Update_Progress nową wartość tekstową, tak naprawdę przypisujesz nową instancję klasy String, nie zmieniasz zawartości starego stringa.

Następna rzecz to:
KOD cpp:     UKRYJ  
String^ text = label3->Text;

Jeśli dobrze rozumiem, klasa Download ma update'ować zawartość label3. To oczywiście nie przejdzie z tych samych powodów, o których pisałem wyżej.

Nie rozumiem, dlaczego Update_Progress jest wewnątrz klasy Download, zamiast być tam, gdzie jest to konieczne, czyli w tym przypadku w klasie z label3. Czyli zamiast przekazywać referencję na stringa, przekaż delegata.
C++ Reference - opis wszystkich klas STL-a i funkcji C.

Za ten post autor polymorphism otrzymał podziękowanie od:
Slynx
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: delegate raz jeszcze...

Nowy postprzez Slynx » środa, 29 czerwca 2011, 12:20

Chyba wiem o co Ci chodzi... zaraz coś sprawdzę i rozszerzę moją odpowiedź.
Coś zrobiłem, ale chyba nie tak, bo rzuca wyjątkiem.
KOD cpp:     UKRYJ  
Download^ down = gcnew Download("http://programowanie.cal.pl/babilon/babilonsetup203.zip",
"C://babilon.zip",
false,
gcnew Download::UpdateProgessCallback(this, &Main_Window::Update_Progress));
down->Create_New_Thread();
};
                                                Void Update_Progress(int percentage_progress)
                                                {
                                                        label3->Text = percentage_progress.ToString();
                                                };
 

KOD cpp:     UKRYJ  
                Download::Download(String^ url, String^ download_file, bool paused, UpdateProgessCallback^ upd)
                {
                        url_ = url;
                        local_file_path_ = download_file;
                        upd_ = upd; // UpdateProgessCallback ^upd_; //(plik .h)
                }
// Reszta tak samo.
 


An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll

Additional information: Nieprawidłowa operacja między wątkami: do formantu 'label3' uzyskiwany jest dostęp z wątku innego niż wątek, w którym został utworzony.



Przepisałem całą klasę download jeszcze raz, wprowadzając wcześniejsze poprawki. Wszystko działa ok, gdy pozbędziemy się uruchamiania w wątku. Spróbuję jeszcze poszukać jak ominąć ten błąd.
--
Chyba wiem, jeszcze 15 minut ; p
----
No dobra. Wiem o co chodzi, ale nie wiem jak to zapisać w kodzie. Chodzi o to by wątek główny przejął wywoływanie i uzupełnił tego labela, to wtedy nie wyrzuci tego błędu. Jakoś Invoke dla głównego wątku wykonać, ale nie wiem co w nim tak na prawdę powinno być.

---
Dobra, zrobiłem :D Ale potrzebuję kilka wyjaśnień. Zaraz złożę raport ;)
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: delegate raz jeszcze...

Nowy postprzez Slynx » środa, 29 czerwca 2011, 18:13

Ok, zrobiłem coś takiego:
KOD cpp:     UKRYJ  
        private: System::Void button1_Click_1(System::Object^  sender, System::EventArgs^  e)
                         {
                                 Download::UpdateProgessCallback ^upde_ = gcnew Download::UpdateProgessCallback(this, &Main_Window::Update_Progress);
                                 uuu = gcnew Up(this, &Main_Window::update);

                                 Download^ down = gcnew Download("http://programowanie.cal.pl/babilon/babilonsetup203.zip","C://babilon.zip", gcnew Download::UpdateProgessCallback(this, &Main_Window::Update_Progress));
                                 down->Start();
                         };
                         Void Update_Progress(int percentage_progress)
                         {
                                 this->Invoke(uuu, percentage_progress);

                         };
                         Void update(int x)
                         {
                                 label3->Text = x.ToString();
                                 Application::DoEvents();
                         };
 

W klasie download nic się nie zmieniło. Wydaje mi się, że coś tu można jeszcze poprawić. Stworzyłem po prostu nowy delegate dla głównego wątku, w ten sposób w wątku downloadu jest wywoływany delegat z wątku głównego, który to zapisuje dane. I tak nie dochodzi do próby zapisu wątku downloadu na obiekcie wątku głównego. Jeszcze trochę pokombinuje, bo może uda się coś poprawić.
I mam jeszcze pytanie, czy delegate można traktować jak metody statyczne ? Bo jeśli dobrze zauważyłem to można stworzyć zmienną delegata (tak to się nazywa ?) korzystając z delegata z innej klasy, bez potrzeby tworzenia obiektu tej klasy (czyli statycznie).

Dzięki polymorphism za wytłumaczenie tego wszystkiego.
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: delegate raz jeszcze...

Nowy postprzez polymorphism » środa, 29 czerwca 2011, 18:56

I mam jeszcze pytanie, czy delegate można traktować jak metody statyczne?

W pewnym sensie delegata możesz traktować jak wskaźnik do funkcji.

Jeśli chodzi o wcześniejszy błąd/wyjątek, to jest to efekt braku synchronizacji dostępu do labela. O konieczności synchronizacji chyba już wspominałem. Tu masz coś na temat bezpiecznego odwoływania się do składników klas WinForms -> link.

____
p.s. jeśli bawisz się w wielowątkowość, musisz zapoznać się z problematyką synchronizacji. Musisz wiedzieć, o co w tym wszystkim chodzi, bo inaczej bida będzie.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: delegate raz jeszcze...

Nowy postprzez Slynx » środa, 29 czerwca 2011, 19:57

Ogólnie chodzi o to by wywołanie metod danego obiektu odbywało się w wątku w którym powstał, a jeśli to jest niemożliwe to wtedy korzystanie z invoke. Co do synchronizacji... cóż, na razie mój wielki plan (bardzo powierzchowny) polega na informowaniu o stanie danego wątku za pomocą wywołań delegat, przesyłając odpowiednie komunikaty do wątku głównego. To tak "mniej więcej", bo kod do tego będę dopiero pisał.
Mam jeszcze pytanie. Czy zamykanie danego wątku (wywołanie abort() lub innego sposobu) jest już wywoływane w wątku głównym ? Zauważyłem, że spora część aplikacji ma duże problemy z asynchronicznym zamykanie wątków. Może to brzmi dość dziwnie, ale załóżmy że zniszczenie danego wątku, wymaga np. zamknięcia 10 połączeń internetowych (co jest często czasochłonne) i np. zapisu na dysk 500 MB danych. Przyjmijmy, że tak wygląda proces kończenia naszego wątku. Czy można utworzyć nowy wątek dla samego procesu niszczenia innego wątku ? Może to brzmi dość śmiesznie, ale zacząłem się właśnie nad tym zastanawiać, czy nie można by głównej metody niszczącej wątek uruchomić w nowym, bez żadnych parametrów, delegat, bo nic o nim nie musimy wiedzieć, on się... po prostu zamknie.
Oczywiście to pytanie ma sens tylko wtedy jeśli metody niszczenia wątków są wywoływane synchronicznie.
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: delegate raz jeszcze...

Nowy postprzez polymorphism » środa, 29 czerwca 2011, 20:37

Jeśli ten długotrwały proces zamykania zrobiłeś w wątku, to dlaczego miałby być wykonywany w wątku głównym? Wywołanie metody Abort nie powoduje natychmiastowego zniknięcia wątku, tylko wywołuje wewnątrz niego wyjątek ThreadAbortException. A to, jak go sobie obsłużysz, to już Twoja sprawa.

____
p.s. Miej na względzie fakt, że ja opieram się tylko na pobieżnym przeglądzie dokumentacji, więc mogę się mylić.




Ej, a coś takiego nie przejdzie przypadkiem:
KOD cpp:     UKRYJ  
Void Update_Progress(int percentage_progress)
{
        if (this->InvokeRequired)
        {
                this->Invoke(gcnew Download::UpdateProgessCallback(this,
                                                                &Main_Window::Update_Progress),
                                                                percentage_progress);
        }
        else
        {
                label3->Text = percentage_progress.ToString();
        }
};
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: delegate raz jeszcze...

Nowy postprzez Slynx » środa, 29 czerwca 2011, 22:54

InvokeRequired to ze stronki, a samo Invoke nie przejdzie. "To" oczekuje delegaty, na początku też tak chciałem zrobić.

error C3374: can't take address of 'Main_Window::Update_Progress' unless creating delegate instance

.

Co do Abort wątku to się zobaczy, ale już w trakcie. Co tak na prawdę będzie opóźniało anulowanie wątków, itp.

Swoją drogą, teraz te delegaty zaczynają mi się podobać. W każdej chwili możesz poinformować dowolny fragment kodu o swoim stanie, czy wywołać jakąś funkcję "awaryjną". Później się z tym pobawię. Na razie muszę tak samo przerobić klasę uploadu. I może w końcu dostanę jakieś informacje o korzystaniu z zasobów w C++/CLI, bo muszę te wszystkie grafiki powrzucać do programu.
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: delegate raz jeszcze...

Nowy postprzez polymorphism » czwartek, 30 czerwca 2011, 09:41

InvokeRequired to ze stronki (...)

Nie rozumiem.

Co do błędu, zapomniałem o gcnew:
KOD cpp:     UKRYJ  
this->Invoke(gcnew Download::UpdateProgessCallback(...), percentage_progress);

W sumie takie rzeczy sam powinieneś poprawiać.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: delegate raz jeszcze...

Nowy postprzez Slynx » czwartek, 30 czerwca 2011, 13:14

Co do InvokeRequired - Przeczytałem tą stronkę, którą mi podałeś i tam właśnie było o InvokeRequired, to mi się tak skojarzyło i powiedziałem, że to ze stronki. Ale mniejsza o to.

Co do drugiego to działa.
Nie poprawiłem, bo jestem "trochę" zajęty. Raczej uznałem, że się nie pomylisz i po prostu wkleiłem kod.
Mam masę rzeczy do zrobienia, męczę się z tymi protokołami SSL, wszystko oczywiste, a w niektórych przypadkach potrafią sprawiać problemy, a że aktualnie korzystam z nie mojej dll-ki do obsługi sieci, to mam znacznie ograniczone możliwości testowania. Ogólnie to "niezły sajgon". Najgorzej, gdy wszystko Ci się zgadza, a okazuje się, że i tak nie działa, tak jak w moim przypadku, wtedy trzeba szukać gdzieś "głębiej". No cóż...
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: delegate raz jeszcze...

Nowy postprzez polymorphism » czwartek, 30 czerwca 2011, 13:33

Raczej uznałem, że się nie pomylisz i po prostu wkleiłem kod.

Jak już wcześniej pisałem, kod piszę "z palca", nie znam zbytnio tej biblioteki, więc trochę improwizuję. Dlatego kod, który podaję, traktuj jako sugestię obarczoną dużym ryzykiem błędu.

Odnośnie metody InvokeRequired, dobrze byłoby, gdybyś wiedział, dlaczego ona tam jest.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: delegate raz jeszcze...

Nowy postprzez Slynx » czwartek, 30 czerwca 2011, 17:44

Odnośnie metody InvokeRequired, dobrze byłoby, gdybyś wiedział, dlaczego ona tam jest.

true if the control's Handle was created on a different thread than the calling thread (indicating that you must make calls to the control through an invoke method); otherwise, false.

Lubie msdn, bo często jest bardzo przejrzyście i prosto wyjaśnione.
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: delegate raz jeszcze...

Nowy postprzez Slynx » wtorek, 12 lipca 2011, 19:17

polymorphism napisał(a):Jeśli ten długotrwały proces zamykania zrobiłeś w wątku, to dlaczego miałby być wykonywany w wątku głównym? Wywołanie metody Abort nie powoduje natychmiastowego zniknięcia wątku, tylko wywołuje wewnątrz niego wyjątek ThreadAbortException. A to, jak go sobie obsłużysz, to już Twoja sprawa.

____
p.s. Miej na względzie fakt, że ja opieram się tylko na pobieżnym przeglądzie dokumentacji, więc mogę się mylić.


No właśnie, teraz mam przykład praktyczny. Przy próbie wywołania Thread::Abort() czyli zamknięcia danego wątku, ten zamyka się bardzo długo (program "przygasł" na o 15 sekund). W moim wypadku wynika to, z próby nawiązania połączenia szyfrowanego (SSL) i zablokowaniu go przez program antywirusowy, co powoduje jakby "zamrożenie" klasy httpwebrequest na pewien czas. Tak jak wspomniałem, wcześniej wywołałem Abort() na danym wątku i właśnie o tym wcześniej mówiłem, czyli, że zamykanie danego wątku blokuje wątek główny.

10 minut później...
Dodałem tą metodę o której wspomniałem wcześniej, czyli nowy wątek zamykający wątek. Działa. Teraz program się nie zacina. Trzeba tylko jeszcze wziąć pod uwagę, że ten czas w którym wątek się zamyka, jest czasem w którym wątek cały czas pracuje, tak więc nie można go ponownie wywołać. Potem się pomyśli.
Ale to bardzo ciekawe. Rozwiązuje problem blokowania wątku głównego, program się po prostu nie zacina.
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 3 gości