[Delphi] Bład przy tworzeniu wątku

0

Napisałem wątek który co 7 minut szuka dane pliki w katalogu i usuwa stare. Jednak co jakis czas (przeważnie co 4 uruchomienie aplikacji) wywala błąd z Thread.

W OnCreate mam:

If not Assigned(Czysc) then
 begin
  Czysc := TCzysc.Create(False);
  Czysc.Priority := tpidle;
 end;

Natomiast cały wątek wygląda tak:

procedure TCzysc.Execute;
 var
  SR : TSearchRec;
  Found : Integer;
begin

  FreeOnTerminate := True;

  while not (Application.Terminated) or (Terminated) do
   begin
   if (Usuwaj=True) then
   BEGIN
     Found := FindFirst(ExtractFilePath(Application.ExeName) + 'System\Plugins\ContactState-Rejestry'+IntToStr(LogUser.AqqNumber)+'\*.txt', faAnyFile, SR); // odnajdz
      while (Found = 0) do // dopoki liczba znalezionych plikow nie bedzie rowna 0
       begin
        if DaysBetween(Now, StrToDateTime(StringReplace(SR.Name, '.txt', '', [rfReplaceAll])) )>Dni then
        DeleteFile(ExtractFilePath(Application.ExeName) + 'System\Plugins\ContactState-Rejestry'+IntToStr(LogUser.AqqNumber)+'\'+SR.Name);
        Found := FindNext(SR); // szukaj dalej
       end;

    Sleep(450000);
    END;
   end;
FindClose(SR); // zakoncz wyszukiwanie
end;

Ktoś wie gdzie może być błąd?

---EDIT---
Jak skopiowałem ów wątek do zwykłej procki to w DeleteFile wywalilo mi że String nie kompatybilny z Char. Zamieniłem i jest ok, ale za to kompilator przy FindClose(SR) wywala że Incopatible Cardinal and TSearchRec. Dlaczego?

0
  1. prostsza jest taka konstrukcja i odpada Ci jedna zmienna
if FindFirst(...) = 0 then
begin
  repeat

  until FindNext(searchResult) <> 0;
  FindClose(searchResult);
end;
  1. tu if (Usuwaj=True) then nie musisz robić porównania - wystarczy samo if Usuwaj then

  2. daj zamiast FindClose SysUtils.FindClose - FindClose to nazwa dwóch różnych procedur (znaczy robią to samo ale mają różne parametry) - jedna to delphiowa a druga to windowsowa

0

Dobra juz znalazlem odpowiedz na forum (SysUtlis - Misiek tnx :) ) i wątek zachowuje sie w miare stabilnie. Ale czemu kompilator w wątku nie pokazał mi tych blędów? Mam także inne pytanie, jak mam Sleep ustawiony na 7 minut to jak to jest z wyładowaniem wątku jak ktoś zamknie aplikacje gdy jest sleep? Watek chodzi przez te kilka minut w pamieci a potem sprawdza czy aplikacja jeszcze działa i dopiero wtedy sie wyładowuje?

0
Dibo84 napisał(a)

Ale czemu kompilator w wątku nie pokazał mi tych blędów?

bo pewnie w unicie z wątkiem miałeś powiedzmy uses SysUtils, a w innym nie albo coś takiego

0

zależy jak potraktuje go system - może łaskawie zaczekać albo wywalić go na chama. Lepiej zamiast czekać 7 minut daj np. pętlę od 1 do 280 i w środku czekaj sekundę i sprawdzaj czy aplikacja jeszcze działa

0

Osobiście polecam wstawienia licznika (inkrementowanego przejściem pętli) do zadziałań pętli, wstawienie sleepa na małą wartośc 1-5s i w środku zrobienie
if licznik="zaplanowane 7 minut" (mozna np w ini zmeiniac) then
begin
licznik=0;
"wykonaj potrzebna procedure";
end;

A jak zamykasz aplikacje to najpierw watek.terminate potem sleep taki aby watek zdazyl sie zakonczyc (ew. petla i zmienna ustawiana juz poza petla watka podczas jego zamykania), i dopiero koniec programu.
Dosc estetyczne, od sleepa w watku zalezy jak bardzo obciazajace serwer (przy 1 s prawie wcale).
Andrzej Dabrowski

0

Hm... troche to zakręcone, ale skoro przy zamykaniu nie wywala żadnego błędu to oznacza że wszystko ok, co? Nie bede sie wtedy z tym meczył... Aha jeszcze coś, czy "Wątek.Terminate" przerywa stan uśpenia Sleep?

0

Oczywiście że nie.
Co do zakręcenia - masz watek z wewnętrznym licznikiem i usypianiem co sekunde. Jeśli licznik dojdzie do załozonej wartosci (odpowiadającej np. 7 minutom) jest zerowany i uruchamai się założona procedure.
Co do zamykania - musisz zawsze sprawdzic czy watek sie zamknal. Za zdanie typu "skoro przy zamykaniu nie wywala żadnego błędu to oznacza że wszystko ok, co? " dostalbys w moim zespole przynajmniej opieprz. Nigdy nie wolno pozwolic by cos niezamierzonego zostalo po twoim programie, albo aby nie zwolnic wszelkiej zarezerwowanej pamieci. Przykład: obsługuj port po TCP/IP w watku, zamknij swoj program w sposob "nie zglaszajacy błedu" i odpal go ponownie. Na 99% nie uda sie otworzyc ponownie portu bo .... jakis piiiiii nie zamknął poprzednio wątku i poprzez to nie zwolnił portu. Teraz wszystk ozależy od systemu operacyjnego i używanych programów, czy odblokują port czy nie. W przypadku skrajnym - restart komputera dopiero pomoze. Wszystko przez to że ktos uznał że "skoro przy zamykaniu nie wywala żadnego błędu to oznacza że wszystko ok?
Dlatego podaję najprostszy sposób na zamknięcie wątku: przy zamykaniu robisz watek.terminate potem czekasz dluzej niz czas petli w watku (aby watek zauwazyl komende do zakonczenai dzialania i się wyłaczył) i dopiero zamykasz. Możesz dobrac czas oczekiwania metodą prób i błedów - powiedmy czas sleepa głównego programu (pomiedzy watek.terminate i aplication.terminate) powiedzmy czas_sleepa_petli_watku+1s. Można sprawdzać np. wyrzucając do logu tekstowego po pętli głownej watku komunikat typu "zamykam watek". Wtedy widzisz czy zdążył się zamknąć czy nie.
Nie wolno, powtarzam nie wolno zdawać się na delphi że zabije wątek, bo nigdy nie wiesz jaki zostanie smietnik po twoim programie, co zostanei po obiektach uzywanych i obslugiwanych przez watek. Dodam że kompilator delphi moze ulec zmianie i np. zmieni się obsługa zostawionych samymi sobie wątków i program który konczyl się bez błedów nagle zacznie...
Podsumowując to co mówisz jest powaznym błedem programistycznym

0

Co do zakrecenia, kawalek kodu:

var
test:boolean;//przy create true;
zadana_liczba_sekund;// po wczytaniu z pliku ustawien 7 minut=420

procedure TWatek.Execute;
var
licznik:integer;
begin
FreeOnTerminate := True;
licznik:=0;
while not Terminated do
begin
licznik:=licznik+1;
if licznik=zadana_liczba_sekund then
begin
licznik:=0;
Synchronize(sprawdzaj_katalogi);
end;
Sleep(1000);
end;
{ tutaj jeszcze powinno byc zwalnianie i/lub zamykanie wszystkich obiektów tworzonych/uzywanych przez watek}
test:=false;
// Dla pewnosci czy dziala mozna dodac:
wyrzuc_do_pliku_tekstowego('Zakończyłem działanie');
end;

procedure zamknij_aplikacje;
begin
watek.Terminate;
while test=false do sleep(500); // ew. sleep(2500);
application.Terminate;
end;

Generalnei całe skomplikowanie - dodam ze nizbyt ladne jest rozwiazanie z test- jako zmienna globalna, zwlaszcza ze przy wielu watkach trzbaby zalozyc synchronizacje i ustalic zmienna jako integer aby sprawdzic czy wszystkei uruchomione watki sie pokonczyly itd... ale od tego sa ksiązki i odrobina oleju w glowie

0
daban napisał(a)

...
Synchronize(sprawdzaj_katalogi);
...

nie ma to jak w wątku wywoływać synchronize, które odwala całą czasochłonną robotę... wiesz co zrobi synchronize? to samo, gdyby sprawdzaj_katalogi było podczepione po prostu pod timer. więc to nie tak się robi.
co do czekania w pętli - to jest mocno prymitywne rozwiązanie.
mniej prymitywne - powinno się czekać na eventa, zapalanego w momencie zakończenia programu, i jednocześnie na drugiego eventa od timera.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/waitformultipleobjects.asp - czekanie na wiele eventów
http://msdn.microsoft.com/library/en-us/dllproc/base/createevent.asp?frame=true - wysyłanie eventa
http://msdn.microsoft.com/library/en-us/dllproc/base/createwaitabletimer.asp?frame=true - stworzenie timera

0

Co do synchronize zgodze się z Tobą, wrzuciłem pierwszy lepszy przykład z przeglądanych ostatnio i nie analizowałem go zbytnio (mój bład, nie chciało mi sie szukać po katalogach swojego programu).
Co do drugiego zarzutu - przeczytałeś co było napisane pod kodem?
"Generalnei całe skomplikowanie - dodam ze nizbyt ladne jest rozwiazanie z test- jako zmienna globalna, zwlaszcza ze przy wielu watkach trzbaby ... ale od tego sa ksiązki i odrobina oleju w glowie"
Nie ma sensu dawać gotowca, zwłaszcza że i tak dużo dostał, zwłaszcza że jego poziom określa fakt że mu wisi czy wątek się skończy czy nie. Podałem najprostsze - niekoniecznie najładniejsze - rozwiązanie by zakumał o czym mówię. Nie mam zamiaru robić wykładu o wielowątkowości itd itp, pokazuję drogę którą powinien podążać i tyle. Usiłowałem wytłumaczyć że można i trzeba zrobić coś aby nie zamykać aplikacji z chodzacym wątkiem...

Poruszę ponownie temat który co jakiś czas wałkuje nalezy podawać rozwiązania odpowiednie dla poziomu pytającego - już kiedyś z MisiekiemD o tym dyskutowałem. Jak myślisz co zrobi pytający (np czy terminate przerywa sleep) po otworzeniu twoich linków? Jak dla mnie rybkę i zostawi aplikację tak jak była - bo cytuję :"skoro nie zamyka się z błędami to jest ok". Napisałem pod spodem że podawane rozwiązanie jest prymitywne - ale na poziomie pytacza i do ogarnięcia przez niego. Ale oczywiście niektórzy musza się przyczepić (co do synchronize się zgadzam - lepiej użyć muteksów), sęk w tym że nadal uważam że jesteśmy tutaj po to aby pomóc innym, a nie po to by popisywac się swoją wiedzą. Dlatego też nie szukałem swoich programów bo musiałbym sporo kodu czyścić tylko chwyciłem pierwszy lepszy z nadesłanych przez kandydatów do pracy - aby mieć podręcznikowy kawałek kodu, taki którego można użyć i który będzie działał.
Na pierwszy ogień dajmy pytaczowi przykład w którym wątek będzie zamykany przed zakończeniem programu, jak zrozumie w czym rzecz to można mu powiedzieć (napisałem że rozwiązanie nieeleganckie - aby nikt sie nie czepiał...) to teraz aby nie robić tego tak topornie wymyślono ... i możesz to zrobić tak... Ale powtarzam najpierw pytacz musi zrozumieć że wątki koniecznie trzeba pozamykać przed zamknięciem aplikacji i że nie jest to zbyt trudne - uruchomić i odpalić program. Jak dostanie zbyt skomplikowany program, albo mało mówiace podpowiedzi to po prostu się zniechęci.
Tak się powinno pomagać - rozwijać po kawałku. Po to studentom daje się najpierw do zaimplementowania wszelkiego rodzaju listy i wstawianie/usuwanie w strukturach dynamicznych by je zrozumieli, a potem pokazuje że to samo można zrobić jedną komendą. Potem już student używa świadomie (przynajmniej powinien) struktur dynamicznych - bo wie jak działają, a nie na zasadzie mam funkcję, komponent znam 3 komendy i ich używam. Taki student nie zapyta o komponent typu TQuake, TWindows ..itp

// ok, ok - Ł

0

Dobra dzięki wszystkim za pomoc. Wątków używam sporadycznie dlatego niewiele o nich wiem. Człowiek uczy sie na błędach :) . A kod przeczytałem na szybkiego i z początku nie złapałem zajawki ale teraz już jest wszystko jasne...

A daban mam jeszcze pytanie bo widze że kumaty Jesteś w tych sprawach. Otóż żeby nie było że lekceważe wyładowania zmiennych to zawsze o to dbam. Mam tylko pytanie. Czasami korzystam z zmiennej TStringStream. Zamykam kod w Try...Finally...StringStream.Free. Jest ok, ale raz chciałem dla upewnienia dać w OnClose formy ponowne wyładowanie ów zmiennej (tak dla pewności), ale tylko w przypadku gdy nie zostala ona wyładowana. Dałem coś takiego:

if assigned(Zmienna_StringStream) then
begin
 Zmienna_StringStream.Free;
end;

Ale przy zamykaniu wywalało mi błąd. Czy "assigned" jest nieodpowiednie dla takich zmiennych?

0

Polecam:
Strumienie
Jest sporo dobrych artykułów na 4programmers.net...
Są też książki, a takie rzeczy są dość dobrze wytłumaczone.
Andrzej Dąbrowski

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