Wątek - działa tylko za pierwszym razem

0

Witam,
mam nadzieję że to już ostatni mój problem w najbliższym czasie...
Mam taki kod:

type
  TWatekWysylania = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  public
    constructor Create();
    procedure KoniecWatkuWysylania (Sender: TObject);
  end;

var
 Watek2 :TWatekWysylania ;
constructor TWatekWysylania.Create();
begin
  inherited Create(True);
  OnTerminate:= KoniecWatkuWysylania ;
end;

procedure TWatekWysylania.Execute;
begin
WysylaniePlikow:= True ;
try Form1.IdFTP1.Connect() ;
finally end ;
if Form1.IdFTP1.Connected then
 begin
 Form1.IdFTP1.Put(cache+'\stopnie.txt', 'stopnie.txt') ;
 Form1.IdFTP1.Disconnect ;
 end
else
 begin
 Form1.Label3.Caption:= 'Błąd podczas wysyłania.'+#13+'Zmiany nie zostały zapisane.' ;
 Form1.SpeedButton2.Visible:= True ;
 end ;
WysylaniePlikow:= False ;
end;

procedure TWatekWysylania.KoniecWatkuWysylania (Sender :TObject);
begin
Form1.PoWatkuWysylania ;
end ;

procedure TForm1.PoWatkuWysylania ;
begin
if not (SpeedButton2.Visible) then
 begin
 Label3.Caption:= 'Zmiany zostały zapisane.' ;
 Timer2.Enabled:= True ;
 end ;
end ;
procedure TForm1.FormCreate(Sender: TObject);
begin
IdFTP1.Port:= 21 ;
IdFTP1.Username := 'user';
IdFTP1.Password := 'haslo';
IdFTP1.Host := 'host';

SHGetSpecialFolderPath(0, cache, CSIDL_INTERNET_CACHE, False) ;

Watek2:= TWatekWysylania.Create() ;
procedure Zapisz ;
begin
Form1.Panel2.Visible:= True ;
Form1.Label3.Caption:= 'Trwa wysyłanie plików...' ;
Watek2.Resume ;
end ;

procedure TForm1.Timer2Timer(Sender: TObject);
begin
Panel2.Visible:= False ;
Timer2.Enabled:= False ;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if WysylaniePlikow then
 begin
 Action:= caNone ;
 ShowMessage('Proszę nie zamykać programu podczas zapisywania zmian na serwerze. Może to spowodować błędy i utratę danych.') ;
 end ;
end ;

I działa to tak:

Po pierwszym wywołaniu procedury Zapisz wszystko jest ok, pokazuje się Panel, tekst "Trwa wysyłanie plików...", po czym "Zmiany zostały zapisane." i po chwili (Timer) wszystko znika.

Ale za drugim, trzecim i każdym kolejnym razem pokazuje się Panel i tekst, dane są wysłane, WysylaniePlikow = False, ale nic więcej - nie zmienia się tekst ani nie znika Panel po jakimś czasie.

Wątków używam po raz pierwszy, dlatego domyślam się, że jest niezbyt estetycznie i użytecznie napisany, i powinno być jakoś lepiej - jeśli tak, będę wdzięczny za poprawki i instrukcje ;)
ale najważniejsze dla mnie jest to, by działał poprawnie za każdym razem, tzn. wysyłał plik, zmieniał napis i znikał Panel. bardzo proszę o pomoc.

0

procedure KoniecWatkuWysylania (Sender: TObject); ma być w TForm1 a nie w TWatekWysylania i w ciele wstaw to
if not (SpeedButton2.Visible) then
begin
Label3.Caption:= 'Zmiany zostały zapisane.' ;
Timer2.Enabled:= True ;
end ;

0

tak?

constructor TWatekWysylania.Create();
begin
  inherited Create(True);
  OnTerminate:= Form1.KoniecWatkuWysylania ;
end;

procedure TWatekWysylania.Execute;
begin
WysylaniePlikow:= True ;
try Form1.IdFTP1.Connect() ;
finally end ;
if Form1.IdFTP1.Connected then
 begin
 Form1.IdFTP1.Put(cache+'\stopnie.txt', 'stopnie.txt') ;
 Form1.IdFTP1.Disconnect ;
 end
else
 begin
 Form1.Label3.Caption:= 'Błąd podczas wysyłania.'+#13+'Zmiany nie zostały zapisane.' ;
 Form1.SpeedButton2.Visible:= True ;
 end ;
WysylaniePlikow:= False ;
end;

procedure TForm1.KoniecWatkuWysylania (Sender :TObject);
begin
if not (SpeedButton2.Visible) then
 begin
 Label3.Caption:= 'Zmiany zostały zapisane.' ;
 Timer2.Enabled:= True ;
 end ;
end ;

niestety nadal jest to samo. ale źle napisałem wcześniej - za pierwszym razem wątek działa, wszystko się robi, po wywołaniu zapisu i próby zamknięcia programu jest komunikat, co oznacza, że WysylaniePlikow = True. Ale za drugim razem tak nie jest, tak jakby wątek w ogóle nie był wykonywany.
Zastanawiam się, czy on po prostu tak jakby nie wraca do początku... tzn. nie wykonuje za drugim razem jeszcze raz wszystkiego od początku. bo takie daje efekty.

0

Zmieniłem na:

procedure TForm1.FormCreate(Sender: TObject);
begin
IdFTP1.Port:= 21 ;
IdFTP1.Username := 'user';
IdFTP1.Password := 'haslo';
IdFTP1.Host := 'host';

SHGetSpecialFolderPath(0, cache, CSIDL_INTERNET_CACHE, False) ;
end ;

procedure Zapisz ;
begin
Form1.Panel2.Visible:= True ;
Form1.Label3.Caption:= 'Trwa wysyłanie plików...' ;
Watek2:= TWatekWysylania.Create() ;
Watek2.Resume ;
end ;

i działa. nie wiem, czy to jest najlepsze, co mogłem zrobić, ale skuteczne i nie ma błędów.

0

dodałem drugi wątek, który ma działać równocześnie z pierwszym i, jak mi się zdawało, głównym wątkiem programu. niestety tak nie jest, a to przez, jak sądzę, synchronize.

type
  TWatek = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  public
    constructor Create();
  end ;

type
  TWatekPobieraniaFTP = class(TThread)
  private
    procedure Polacz ;
  protected
    procedure Execute; override;
  public
    constructor Create();
  end;
procedure TForm1.FormCreate(Sender: TObject);
begin
IdFTP1.Port:= 21 ;
IdFTP1.Username := 'user';
IdFTP1.Password := 'pass';
IdFTP1.Host := 'host';

SHGetSpecialFolderPath(0, cache, CSIDL_INTERNET_CACHE, False) ;

if FileExists(cache+'\stopnie.txt') then DeleteFile(cache+'\stopnie.txt') ;

Form1.Timer1.Enabled:= True ;
Watek3:= TWatekPobieraniaFTP.Create() ;
Watek1:= TWatek.Create() ;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
if ProgressBar1.Position = 100 then
 ProgressBar1.Position:= 0 ;
ProgressBar1.Position:= ProgressBar1.Position + 1 ;
end;
constructor TWatek.Create();
begin
  inherited Create(False);
  OnTerminate:= Form1.KoniecWatkuPobierania ;
end;

procedure TWatek.Execute;
var
 url, gdzie :PAnsiChar ;
begin
FreeOnTerminate := True;
Pobieranie[3]:= False ;
url:= PAnsiChar('http://' + Form1.IdFTP1.Host + '/stopnie.txt') ;
gdzie:= PAnsiChar(cache + '\stopnie.txt') ;
                
if URLDownloadToFile(Nil, url, gdzie, 0, Nil) <> 0 then
 Pobieranie[1]:= False
else
 Pobieranie[1]:= True ;
 Pobieranie[1]:= False ;

Pobieranie[3]:= True ;
end;

constructor TWatekPobieraniaFTP.Create();
begin
  inherited Create(False);
  OnTerminate:= Form1.KoniecWatkuPobierania ;
end;

procedure TWatekPobieraniaFTP.Execute;
begin
FreeOnTerminate := True;
Pobieranie[4]:= False ;

Synchronize (Polacz) ;
 
Pobieranie[4]:= True ;
end;

procedure TWatekPobieraniaFTP.Polacz ;
begin
try Form1.IdFTP1.Connect() ;
finally end ;
if Form1.IdFTP1.Connected then
 begin
 Pobieranie[2]:= True ;
 try
  Form1.IdFTP1.Get('stopnie.txt', cache+'\stopnie.txt') ;
 except
  Pobieranie[2]:= False ;
 end ;
 Form1.IdFTP1.Disconnect ;
 end
else
 Pobieranie[2]:= False ;
end ;

zamiast działać równolegle, łączenie z serwerem przez FTP zawiesza okienko do czasu, aż się nie połączy (widać to m.in. po nie działaniu przez ten czas Timera, klepsydrze itd). Dlaczego tak jest i co zrobić, by te 3 procesy - wątek 1, wątek 3 i normalne działanie programu - działały równocześnie?

0

wtf?

if URLDownloadToFile(Nil, url, gdzie, 0, Nil) <> 0 then
 Pobieranie[1]:= False
else
 Pobieranie[1]:= True ;
 Pobieranie[1]:= False ;

co robi to ostatnie false?
to samo, ale krócej i - imho - czytelniej:

Pobieranie[1] := URLDownloadToFile(Nil, url, gdzie, 0, Nil) = 0;

kod działa tak, jak go napisałeś, kazałeś mu synchronicznie wykonywać połączenie, to i tak robi:

Synchronize (Polacz);

napiszę to po raz kolejny: nie używajcie instrukcji, co do których nie macie pojęcia jak działają. jest help, jest google, wiec zawsze można się dowiedzieć.

te 3 procesy - wątek 1, wątek 3 i normalne działanie programu

proces jest jeden, bo wątek to nie proces, a wątki masz nie trzy, a dwa - jeden z TThread i jeden z procesu aplikacji. połącz się asynchronicznie, to problem zniknie. uprzedzam, że indy ma problemy z wielowątkowością, więc proponuję użyć np. Synapse, chyba, że tylko jeden wątek naraz będzie operował na idFtp.

0

to ostatnie false to pozostałość po moich testach. oczywiście nie powinno go być. z tym kodem w jednej linijce - to tak się da? :D dzięki.

tylko ten jeden wątek operuje na idFTP, więc nie ma problemu.

kazałem synchronicznie, ponieważ takie coś:

procedure TWatekPobieraniaFTP.Execute;
begin
FreeOnTerminate := True;
Pobieranie[4]:= False ;

try Form1.IdFTP1.Connect() ;
finally end ;
if Form1.IdFTP1.Connected then
 begin
 Pobieranie[2]:= True ;
 try
  Form1.IdFTP1.Get('stopnie.txt', cache+'\stopnie.txt') ;
 except
  Pobieranie[2]:= False ;
 end ;
 Form1.IdFTP1.Disconnect ;
 end
else
 Pobieranie[2]:= False ;
 
Pobieranie[4]:= True ;
end;

nie działa - wyrzucało błędy. więc przyznaję się, kombinowałem ;)

oto błędy, które dostaję po takim kodzie:

Exception EAccessViolation in module Project1.exe at 0001AF1A.

Access violation at address 0041AF57 in module 'Project1.exe'. Read of address 0000000E.

i

Exception EOSError in module Project1.exe at 0000D612.

System Error.  Code: 5.

Odmowa dostępu.

jak to powinno wyglądać poprawnie?

0

dlaczego nie mogę pobierać za pomocą IdFTP w wątku? jest mi to potrzebne, bo nie chcę, żeby pobieranie zawieszało program, a przy normalnym wywołaniu pobierania w wątku (patrz post wyżej) wyskakują błędy.

jest mi to potrzebne, więc bardzo proszę o pomoc.

0

szczerze mówiąc, nie mam pojęcia. indy nie jest thread-safe i prawie na pewno przez to masz problem, natomiast przez co konkretnie w tym przypadku - no idea. proponuję skorzystanie z innej biblioteki, np. synapse.

0

eh, z synapse dotąd nie korzystałem. będę musiał trochę powalczyć pewnie ;)
to takie 2 podstawowe pytanka:

  • jak to zainstalować? pobrałem z http://www.synapse.ararat.cz/doku.php/download i w source/lib są same pliki .pas :/
  • potem jakiego komponentu użyć, by tylko pobrać plik z serwera? nie muszę się nawet logować przez FTP, pobranie za pomocą innego protokołu mi wystarczy, szczególnie że bez logowania tylko po prostu ściągnięcie pliku z określonego adresu będzie szybsze.
0

Takie pytanie biednego programisty wszystkiego innego niż Delphi - to do Synapse nie ma readme/dokumentacji?

0

już sobie poradziłem z tym, by synapse działało mi w delphi, ale wciąż nie wiem jak pobrać z neta plik? :/

0

Przecież - o ile się nie myle - to do Synapse są dema.

0

pół dnia się męczyłem, i udało mi się na 3 sekundy przed tą odpowiedzią z demami ;)
a dema tak, są, ale niewiele mi dały. w końcu znalazłem w necie. póki co działa, jeszcze zobaczymy, co będzie jak wrzucę pobieranie w wątek...

0

jak co to korzystaj z komponentów tworzonych dynamicznie w wątku. Szczególnie jeżeli będziesz miał kilka wątków. Jeżeli masz ich naprawdę dużo możesz użyc tablic do przechowywania komponentów typu idFTP czy idHTTP itp

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