TTimer, blokowanie w głównym watku

Odpowiedz Nowy wątek
2020-05-15 12:37

Rejestracja: 17 lat temu

Ostatnio: 9 godzin temu

0

Witam,
Mam być może banalny problem, a może nie. Otóż, mój program (pomocnik w kompilacji źródeł) wykonuje wiele różnych operacji, czasami wymagających. Na formie wyświetlam całkowity czas działania różnych operacji, który odświeżany jest w czasie rzeczywistym (a program po prostu zbiera informacje z różnych plików tworząc nowe, w pętlach, często zagnieżdżonych, odpala kompilację podprogramów Delphi (w linii komend, przy użyciu MSBuild), odpala zewnętrzny kompilator (NSIS, tworzy instalator) i różne takie bzdety).

Do tak prostej operacji jak wyświetlanie czasu używam standardowych komponentów, TTimer oraz TLabel. Ale, jest tutaj PROBLEM - jeśli program wykonuje jakieś bardziej wymagające operacje (wystarczy pętla po wielu elementach) czas nie odświeża się. Zajęty główny wątek programu uniemożliwia działanie timera.

procedure TMainFrm.TimerTimer(Sender: TObject);
begin
   MainFrm.Lbl_StopTime.Caption := TimeTostr(Now);
end;

Teoretycznie może pomóc instrukcja Application.ProcessMessages. Ale, niestety - to też nie pomaga.

Jakie jest rozwiązanie tego wydawałoby się banalnego problemu? Z pewnością czegoś nie wiem.
Szukając informacji znalazłem ciekawe rozwiązanie z osobnym wątkiem i odpalaniem zdarzenia, coś takiego (powinno działać, ale nie działa!):

type
  TTimerThread = class(TThread)
  private
    FTickEvent: THandle;
    procedure ProcessGUI;
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;
    procedure FinishThreadExecution;
  end;

type
  TMainFrm = class(TForm)

  private
    { Private declarations }
    FTimerThread: TTimerThread;
  public
    { Public declarations }
  end;

//...

procedure TMainFrm.FormCreate(Sender: TObject);
begin
   FTimerThread := TTimerThread.Create(False);
end;

procedure TMainFrm.FormDestroy(Sender: TObject);
begin
   FTimerThread.FinishThreadExecution;
end;

{ TTimerThread }
constructor TTimerThread.Create(CreateSuspended: Boolean);
begin
   inherited;
   FreeOnTerminate := True;
   FTickEvent := CreateEvent(nil, True, False, nil);
end;

destructor TTimerThread.Destroy;
begin
   CloseHandle(FTickEvent);
   inherited;
end;

procedure TTimerThread.FinishThreadExecution;
begin
   Terminate;
   SetEvent(FTickEvent);
end;
procedure TTimerThread.Execute;
begin
   while not Terminated do
      begin
         if WaitForSingleObject(FTickEvent, 100) = WAIT_TIMEOUT then
            begin
               Synchronize(ProcessGUI);
            end;
      end;
end;
procedure TTimerThread.ProcessGUI;
begin
         Application.ProcessMessages;
         MainFrm.Lbl_StopTime.Caption := TimeTostr(Now);
end;

Proszę o jakiś konstruktywny przykład rozwiązania tego problemu.
(Wyświetlenie czasu operacji, podczas np. dodawania 100 000 operacji w pętli, czy cokolwiek zajmującego główny wątek).

-Pawel


Zapraszam:
http://www.pawelporwisz.pl
Potrzebujesz instalator? Napiszę go za Ciebie (NSIS).
Potrzebujesz program? Napiszę go za Ciebie (Delphi).
edytowany 1x, ostatnio: furious programming, 2020-05-15 14:50

Pozostało 580 znaków

2020-05-15 13:07

Rejestracja: 6 lat temu

Ostatnio: 11 godzin temu

2

Jeżeli dalej wykonujesz swoje operacje na plikach w głównym wątku GUI to się nie odświeży bo jest zajęty. Najlepiej przenieś te operacje do innego wątku.

Pozostało 580 znaków

2020-05-15 16:43

Rejestracja: 17 lat temu

Ostatnio: 9 godzin temu

1

Czy sugerujesz przenieść wszystkie operacje do innego wątku, a w głównym pozostawić timer?
Bo w obecnym scenariuszu, gdzie większość działa w głównym wątku, a timer działa w osobnym wątku nie działa to.


Zapraszam:
http://www.pawelporwisz.pl
Potrzebujesz instalator? Napiszę go za Ciebie (NSIS).
Potrzebujesz program? Napiszę go za Ciebie (Delphi).

Pozostało 580 znaków

2020-05-15 18:26

Rejestracja: 15 lat temu

Ostatnio: 2 minuty temu

Lokalizacja: Gorlice

3

Główny wątek aplikacji odpowiedzialny jest za przetwarzania kolejki komunikatów i zablokowanie go powoduje zamrożenia apki. W VCL praktycznie wszystko opiera się na WinApi a więc właśnie w dużym stopniu na obsłudze kolejki komunikatów. W głównym wątku aplikacji zrób wątek poboczny na czasochłonne operacje i będzie śmigało. Tylko gdybyś potrzebował z tego pobocznego wątku dobrać się do komponentów VCL to musisz to zrobić przy użyciu jakiejś metody synchronizacji ale to już inna sprawa.


Nie odpowiadam na PW w sprawie pomocy programistycznej.
Pytania zadawaj na forum, bo:
od tego ono jest ;) | celowo nie zawracasz gitary | przeczyta to więcej osób a więc większe szanse że ktoś pomoże.

Pozostało 580 znaków

2020-05-15 21:01
Moderator Delphi/Pascal

Rejestracja: 8 lat temu

Ostatnio: 4 godziny temu

Lokalizacja: Tuchów

1
Pepe napisał(a):

[…] a timer działa w osobnym wątku nie działa to.

Timer nie jest wątkiem pobocznym – on działa w ramach głównego wątku, dlatego jeśli go zablokujesz operacjami trwającymi np. 10 sekund, to timer przez te 10 sekund będzie również zamrożony. I właśnie dlatego powinieneś wszystko przenieść do wątków pobocznych, a zwykłego timera w ogóle nie używać.


edytowany 1x, ostatnio: furious programming, 2020-05-15 21:02

Pozostało 580 znaków

2020-05-15 21:11

Rejestracja: 17 lat temu

Ostatnio: 9 godzin temu

0

Tego się niestety obawiam - zastanawiam się tylko, dlaczego rozwiązanie które pokazałem powyżej nie działa. Przecież odliczanie realizowane jest w osobnym wątku, a kontrolka VCL (TLabel) jest synchronizowana zgodnie ze sztuką...


Zapraszam:
http://www.pawelporwisz.pl
Potrzebujesz instalator? Napiszę go za Ciebie (NSIS).
Potrzebujesz program? Napiszę go za Ciebie (Delphi).

Pozostało 580 znaków

2020-05-16 07:33

Rejestracja: 15 lat temu

Ostatnio: 2 minuty temu

Lokalizacja: Gorlice

0

To się obawiaj a nie próbuj w takim razie nie jesteśmy w stanie pomóc.


Nie odpowiadam na PW w sprawie pomocy programistycznej.
Pytania zadawaj na forum, bo:
od tego ono jest ;) | celowo nie zawracasz gitary | przeczyta to więcej osób a więc większe szanse że ktoś pomoże.
edytowany 1x, ostatnio: kAzek, 2020-05-16 07:35
Tak będzie. Twój komentarz moim zdaniem stylistycznie leży i kwiczy...Nie wniósł też zbyt dużo do dyskusji :P - Pepe 2020-05-16 08:30
Twój na pewno wniósł bardzo dużo. Tylko jęczysz a nie chce ci się nawet sprawdzić na innym, forum taki wątek poleciałby do kosza, bo tak naprawdę tam jest jego miejsce. - kAzek 2020-05-17 07:00
Nie jęczę, jak to ładnie ująłeś tylko szukam wsparcia/pomocy na forum, które do tego jest stworzone. Jak się nie podoba, to nie bierz udziału w dyskusji. Jeśli wątek łamie jakieś zasady - proszę go usunąć. Cenię twoją wiedzę i zaangażowanie w forum, więc dziękuję, że znalazłeś czas na odpowiedź. Pozdrawiam. - Pepe 2020-05-17 09:09

Pozostało 580 znaków

2020-05-16 11:12

Rejestracja: 17 lat temu

Ostatnio: 9 godzin temu

0

A tutaj jeszcze tylko pokaże, jak to działa (nieprawidłowe odświeżanie czasu). Mam nadzieję, że jak pokończę inne ważniejsze sprawy w tym programie, dodam obsługę wątków (jak się uda) i będzie to śmigać. Jak widać, na początku czas w ogóle jest zablokowany, dopiero po wykonaniu bardziej czasochłonnych działań idzie (bo reszta jest odpalana jako osobne procesy).
Wideo (45Mb): https://www.dropbox.com/s/q5y[...]/2020-05-16-11-04-27.mp4?dl=0


Zapraszam:
http://www.pawelporwisz.pl
Potrzebujesz instalator? Napiszę go za Ciebie (NSIS).
Potrzebujesz program? Napiszę go za Ciebie (Delphi).

Pozostało 580 znaków

2020-05-22 02:46

Rejestracja: 1 rok temu

Ostatnio: 6 godzin temu

0

Nie programuję w Delphi, więc nie wiem, czy moją sugestię da się wdrożyć.
Ale w WINAPI są tzw. timery multimedialne:
https://docs.microsoft.com/en[...]/multimedia/multimedia-timers
W szczególności zobacz na funkcję timeSetEvent:
https://docs.microsoft.com/en[...]s-versions//dd757634(v=vs.85)
Taki timer powinien rozwiązać Twój problem.


Pozostało 580 znaków

Odpowiedz

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