Witam,
Nidy nie zdawałem pytań na tym forum bo zawsze znajdowałem odpowiedź w postach innych użytkowników, ale nie tym razem. Mam tablice dynamiczną w której trzymam wątki, i podczas wykonywaniu wątku potrzebuję zwrócić jakąś informację w której muszę użyć indeksu tablicy wykonywanego wątku. Jest jakiś sposób żeby uzyskać taką informację, dla którego indeksu tablicy wątek coś wykonał? Oczywiście zrobiłem to w ten sposób że przekazuje indeks tablicy do wątku pod czas jego tworzenia, ale nie jest to chyba eleganckie, jest jakiś inny sposób?
Ja wolę stworzyć klasę która pochodzi od TThread, wyniki zostają w klasie.
Zastosowałem to rozwiązanie ale mam problem podczas zwalniania klasy. Gdy użyje Free albo Destroy zawiesza mi się cały program a debuger nic nie wyrzuca i nie wiem dlaczego. Przed wywołaniem Free lub Destroy zatrzymywałem działanie wątku i nic, też z własnym zadeklarowanym destructorem czy bez niego też to samo. Dodam że inna klasa w tym samym programie działa tak samo i zwalnianie jej zawiesza program. A robię to tak:
...
type
TObsluga = class(TThread)
private
Indeks: Integer;
protected
procedure Execute; override;
public
constructor Create(ID: Integer);
destructor Destroy; override;
end;
...
constructor TObsluga.Create(ID: Integer);
begin
inherited Create(False);
Self.Indeks := ID;
//jescze jakies inne zmienie ustawiam
end;
destructor TObsluga.Destroy;
begin
//tu jakis komunikat wywoluje
inherited;
end;
procedure TObsluga.Execute;
begin
while not((Application.Terminated) or (Terminated)) do
begin
// coś tam się robi
end;
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
if Assigned(GlownyWatek) then
begin
GlownyWatek.Free;
end;
end;
...
end.
Dodam, że jeśli użyje Terminate zamiast Free a w Execute wstawię przed pętlą while FreeOnTerminate na true to też się program zawiesza
Jeśli ustawisz FreeOnTerminate to wątek zwolni się sam po zakończeniu działania. Użycie Terminate powoduje zmianę Terminated na true i twoim zadaniem w metodzie Execute jest jak najszybsze sprawdzenie tego warunku i wyjście z tej metody. Jeśli tego nie zrobisz wątek nie zostanie zwolniony.
Ale to przecież napisałem pod kodem, że też robiłem w ten sposób, użyłem FreeOnTerminate na True przed while i kończyłem Buttonem za pomocą Terminate a program po tym się i tak zawiesza. Bo jeśli używam Terminate to pętla się przecież kończy
while not((Application.Terminated) or (Terminated)) do
, dodatkowo w while mam jeszcze jedną pętle ale tam też mam warunek Terminated=True wtedy robię break a i tak się wiesza.
while not((Application.Terminated) or (Terminated)) do
while (not Application.Terminated) and (not Terminated) do
Mam dziwne wrażenie że to tak powinno być.
Ten kod nie ma sensu:
if Assigned(GlownyWatek) then
begin
GlownyWatek.Free;
end;
Ponieważ nawet jeśli zwolnisz GlownyWatek to Wskaźnik i tak nie zostanie zamieniony sam na nil.
Mając np. taki wątek:
procedure TTestThread.Execute;
begin
FreeOnTerminate := false;
while not(Terminated) do
begin
Beep;
Sleep(7000);
// coś tam się robi
end;
Sleep(100);
Beep;
Sleep(100);
Beep;
end;
Gdy w głównej aplikacji zaczniesz zwalniać go np. tak:
procedure TForm1.Button2Click(Sender: TObject);
begin
Thread.Free;
Application.Terminate;
end;
To główna forma będzie zacięta do momentu gdy nie wyjdziesz z Execute i dopiero wtedy wątek zostanie poprawnie zwolniony.
Jest możliwość też wymuszenia zamknięcia wątku np. tak:
TerminateThread(Thread.ThreadId, 404);
ale wtedy nie mamy możliwości dowiedzenia się czy wątek zrobił to co powinien jest po prostu ubity.
Ok, dobra doszedłem w czym jest problem wewnątrz wątku mam funkcje która blokuje pętle, jest to accept z Winsock i dopóki nie przyjdzie jakieś połączenie cały wątek wisi. Teraz pytanie czy jest jakaś możliwość wrzucenia accepta w jakiegoś trya czasowego np po 1 sekundzie idzie dalej i zwraca. To
TerminateThread(Thread.ThreadId, 404);
sądzę że zadziała ale będzie to ostateczność po którą wolałbym nie sięgać
Nie wiem na jakim poziomie obsługujesz winsock i czy to są blokujące gniazda czy nie, ale w źródłach TClientSocket czy TServerSocket jest coś takiego jak WaitForData czy TimeOut - zobacz jak to jest tam zakodowane. Nawet jeśli chcesz duży TimeOut np. 30 sekund to zrób sobie zamiast tego 30 razy 1 sekunde TimeOut a po 1 sekundzie sprawdzaj czy coś nie chce zamknąć wątku, wyjdzie na jedno a użytkownik nie będzie musiał czekać zbyt długo na reakcję programu.
...
type
TObsluga = class(TThread)
private
Done:Boolean;
...
procedure TObsluga.Execute;
begin
...
Done:=true;
end;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(GlownyWatek) then
begin
GlownyWatek.Terminate;
while not GlownyWatek.Done do begin end;
end;
end;
...
end.
To nic nie wnosi do rozwiązania problemu bo i tak watek będzie wisiał na funkcji accept. Podejrzałem TIdCustomTCPServer z Indy które korzysta z Winsock i funkcji accept i to nie różni się znacząco od mojego kodu, tylko później używane jest FreeAndNil ale też nie bezpośrednio do klasy gdzie jest użyte accept więc to też nic nie wyjaśnia a przeszukanie wszystkich modułów od indy to kilka dni jak nic. Wymyśliłem że po wysłaniu Terminate od razu wykonam connect i funkcja się odwiesi i zwolni się wtedy cały wątek.
usunięcie cytowania całego poprzedniego posta - fp
jagro napisał(a):
... To nic nie wnosi do rozwiązania problemu ...
No bo problem zupełnie inny niż przedstawiłeś.
Mówiłeś że masz problem z zakończeniem wątku no to dostałeś jak go zakończyć.
Jak masz problem z obsługą serwera to pokaż kod i powiedz jaki masz z tym problem.
Zawsze możesz ustawić TimeOut na powiedzmy 200 ms, więc po 200ms wykona się kolejny krok pętli, wtedy program sprawdzi ten warunek pętliTerminated