TTimer, blokowanie w głównym watku

TTimer, blokowanie w głównym watku
Pepe
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 2 godziny
  • Postów:496
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.

Kopiuj
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!):

Kopiuj
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


edytowany 1x, ostatnio: flowCRANE
hzmzp
  • Rejestracja:ponad 11 lat
  • Ostatnio:około 5 godzin
  • Postów:620
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.

Pepe
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 2 godziny
  • Postów:496
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.


KA
  • Rejestracja:prawie 20 lat
  • Ostatnio:4 minuty
  • 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.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Tuchów
  • Postów:12167
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ć.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
Pepe
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 2 godziny
  • Postów:496
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ą...


KA
  • Rejestracja:prawie 20 lat
  • Ostatnio:4 minuty
  • 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
Pepe
Tak będzie. Twój komentarz moim zdaniem stylistycznie leży i kwiczy...Nie wniósł też zbyt dużo do dyskusji :P
KA
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.
Pepe
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
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 2 godziny
  • Postów:496
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/q5y1tfwinc9m42j/2020-05-16-11-04-27.mp4?dl=0


Stefan_3N
  • Rejestracja:około 6 lat
  • Ostatnio:20 dni
  • Postów:145
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-us/windows/win32/multimedia/multimedia-timers
W szczególności zobacz na funkcję timeSetEvent:
https://docs.microsoft.com/en-us/previous-versions//dd757634(v=vs.85)
Taki timer powinien rozwiązać Twój problem.


Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)