Utworzenie informacji że program się uruchamia

Utworzenie informacji że program się uruchamia
KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0

Witam.
W moim programie jest dużo danych przygotowywanych i przetwarzanych w trakcie uruchamiania. Umieściłem je w sekcji FormCreate. Wszystko działa bardzo bobrze, jednakże pomimo iż mam raczej dobry sprzęt (i7 4GHz), to po uruchomieniu programu przez około 15 sekund wygląda jakby nic się nie działo. Próbowałem też umieścić te procedury w sekcji FormShow, lecz nic to nie zmieniło. Jeśli nie wywołuję tych procedur, to okno aplikacji pojawia się natychmiast po uruchomieniu.
Chodziło by mi o to, aby po uruchomieniu programu pokazywało się okno aplikacji, na którym mógłbym umieścić informację o uruchamianiu np. typu Label, a potem uruchomić te czasochłonne procedury. Zależy mi aby były one wywoływane tylko raz, a nie np OnActivate. Niestety nie udało mi się tego zrobić.
Proszę o pomoc.

axelbest
  • Rejestracja:ponad 17 lat
  • Ostatnio:około godziny
  • Lokalizacja:Warszawa
  • Postów:2251
4

To tak tylko podpowiem (bo nie programuje nic w Delphi), że może by tak ładowanie danych puścić w innym wątku i użytkownikowi wyświetlać info, że dane już załadowanie, a do tego czasu niech się kręci jakiś spinner czy jakieś splash logo? Ma to sens?

Link raczej stary, ale może sam coś lepszego znajdziesz

https://www.experts-exchange.com/articles/6613/Adding-threads-for-loading-data-in-background-to-a-delphi-application.html

edytowany 1x, ostatnio: axelbest
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Tuchów
  • Postów:12164
5
Klakierus napisał(a):

Witam.
W moim programie jest dużo danych przygotowywanych i przetwarzanych w trakcie uruchamiania. Umieściłem je w sekcji FormCreate.

No to już zrobiłeś błąd – nie powinieneś takich rzeczy umieszczać w kodzie formularza. Klasy służące do zarządzania danymi powinny być od nich niezależne i być tworzone na zewnątrz nich.

Wszystko działa bardzo bobrze, jednakże pomimo iż mam raczej dobry sprzęt (i7 4GHz), to po uruchomieniu programu przez około 15 sekund wygląda jakby nic się nie działo.

Czyli na moim komputerze trwało by to z minutę. :]

Nie marnując czasu na buzzwordy, wszystkie klasy służące do przechowywania i zarządzania danymi oddziel od formularzy i zapisz w osobnych modułach. Natomiast wszystkie instrukcje ładujące dane czy je obrabiające (te, które zjadają te piętnaście sekund) wywołaj przed wywołaniem metod tworzących instancje formularzy (czyli pośrednio lub bezpośrednio w głównym pliku projektu).

Po drugie, zrób okno splash, które będzie widoczne na ekranie podczas ładowania danych. Utwórz je dynamicznie, przekazując Application jako Owner. Natomiast aby takie okienko reagowało na komunikaty, cały proces ładowania danych wydziel do wątku pobocznego (albo do kilku).

To tak z grubsza.


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 3x, ostatnio: flowCRANE
robertz68
  • Rejestracja:około 18 lat
  • Ostatnio:37 minut
  • Lokalizacja:Zielona Góra
0

a ja jestem ciekaw (wiem, wiem, ciekawość to pierwszy stopień ...) cóż to za dane są generowane że aż tyle czasu zajmują? Nie jest to przypadkiem coś dociągane z netu?

F8
  • Rejestracja:prawie 18 lat
  • Ostatnio:ponad 4 lata
2

a ja dam prostszy sposób. FormCreate i FormShow wywołuje sie tak na prawde przed wyrysowaniem okna, dlatego najlepszym rozwiązaniem jest stworzenie własnej symulującej coś na krztałt OnAfterShow

Kopiuj
const
  WM_AFTER_SHOW = WM_USER + 300; 
type
  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
  private
    procedure OnAfterShow(var Msg: TMessage); message WM_AFTER_SHOW;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.OnAfterShow(var Msg: TMessage);
begin
  // tutaj wywołaj swoje metody
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  PostMessage(Self.Handle, WM_AFTER_SHOW, 0, 0);
end;
KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0

Dziękuję za rady. Na razie skorzystam z porady kolegi flasher86. To co napisał furious programming muszę w wolnym czasie przemyśleć i zrozumieć, jestem na dużo niższym poziomie, piszę właściwie tylko pomoce dla siebie.
Co do pytania robertz68, to nie dane z netu, to wczytywanie i obróbka grafik w wysokiej rozdzielczości, wstępne przygotowanie kolorów, miejsc pod inne grafiki i napisy itp.
Dziękuję za pomoc.
Pozdrawiam.

edytowany 1x, ostatnio: Klakierus
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Tuchów
  • Postów:12164
0
flasher86 napisał(a):

FormCreate i FormShow wywołuje sie tak na prawde przed wyrysowaniem okna, dlatego najlepszym rozwiązaniem jest stworzenie własnej symulującej coś na krztałt OnAfterShow

No i w czym to niby pomoże? Nadal okno będzie zamrożone i do czasu przetrawienia danych nie będzie odpowiadało na komunikaty. Bo tutaj problemem nie jest moment przetwarzania danych, a wydzielenie tego procesu z głównego wątku, tak aby interfejs nie ”wisiał”.

Poza tym sterowanie interfejsem i zarządzanie oknami z poziomu zdarzeń konkretnego okna (np. głównego okna) to zła praktyka i nie powinno się tego robić. Okna mają żyć własnym życiem, a aplikacja powinna być zarządzana przez mechanizmy niezależne od formularzy.


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 3x, ostatnio: flowCRANE
somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
0

Pewnie chodzi o to, by pojawiło sie okno/splash z info, że coś się dzieje, a po 30 sekundach ożyło. Bo teraz z tego co rozumiem to odpalasz program i bez znaków życia on przez 30 sekund odpala się nie daje znać, że żyje - w ten sposób ,możesz włączyć go drugi raz i zaraz masz całą kolekcje nowych procesów co wiszą i co jakiś czas będą się odpalać ;) Niemniej masz racje - ładowanie danych powinno być asynchroniczne.

KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0

somedev - dokładnie tak. Zrobiłem zgodnie z radą flasher-a i działa poprawnie.
Niestety dotychczas pisałem tylko proceduralnie i właśnie czytam o klasach/wątkach - próbuję nauczyć się tego, o czym napisał furious programming.

KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0

Gdy czytam co pisze kolega furious programming wszystko wydaje się jasne i logiczne. Poczytałem o tworzeniu wątków, troszkę to jest dla mnie zagmatwane, dlaczego nie można wprost uruchomić danej procedury w nowym wątku, należy tworzyć nowe klasy, ale nie ma co narzekać, tylko się nauczyć tego.
Zacząłem więc od przerzucenia procedur i funkcji przygotowujących i obrabiających dane do nowego modułu. Było trochę zamieszania, gdyż stale używane obrazy miałem jako zmienne globalne i użycie ich było niejawne, a tymczasowo używane obrazy były jako zmienne lokalne przekazywane jawnie. Udało się wszystko zrobić bez problemów, wszystkie przekazania zmiennych są teraz jawne.
Jednakże przy próbie tworzenia wątków zgłupiałem i mam kilka pytań. Abym to zrozumiał posłużę się przykładami, a nie moimi procedurami. Jeśli przykładowo mam taki program (piszę naprędce z głowy, nie kopiuję z kompilatora,więc możliwe są błędy):

Kopiuj
var 
     obraz, wzor : TBitmap;
     kolor1, kolor2 : TColor;

procedure TForm1.obrobka(var obraz:TBitmap; wzor:TBitmap; kolor1, kolor2:TColor);
begin
   ...
end;

begin
   obraz:=TBitmap.Create;
   wzor.Picture.LoadFromFile('plik1.bmp');
   obrobka(obraz,wzór,kolor1,kolor2);
   ...
   obraz.Free;
end.

Następnie przerzucam procedurę "obrobka" do nowego modułu i przerabiam, aby później mogła być wywoływana w wątku

Kopiuj
unit procedury;
...
type 
TprocZrobObraz=procedure(obj:TObject; obraz, wzor:TBitmap; kolor1, kolor2:TColor) of object;

procedure obrobka(obj:TObject; procZrobObraz:TprocZrobObraz);

implementation

procedure obrobka(obj:TObject; procZrobObraz:TprocZrobObraz);
var 
   obraz : TBitmap;
begin
   obraz:=TBitmap.Create;
   ...
   procZrobObraz(obj, obraz, wzor, kolor1, kolor2);
   obraz.Free;
end;

Jak w takim przypadku przekazać w programie głównym zmienne do danej procedury?
U mnie kompilator stale wywala że jest niezgodność danych, niemożna przypisać TBitmap do mojego nowego typu danych.
W jaki sposób powinienem deklarować dane które mają być zmienione - odpowiednik "var" (obraz) oraz te które są tylko do odczytu (wzór)?

somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
3

Ależ można używać tego prościej od razu w innym wątku. To że @furious programming mówił o wątkach to sądzę, ze albo jest skrótem myślowym, albo po prostu nadal z tego korzysta, niemniej tutaj nie używał bym wątków, jako takich ale samego konceptu. Wątki jak zauważyłeś są nieco skomplikowane w użyciu, dlatego opakowano je w wyższa abstrakcje. Obecnie można wzorem C#, w prosty sposób tworzyć taski, które w dowolnym momencie mogą zostać utworzone i uruchomią kod do przetwarzania współbieżnego. Taski mają szereg zalet - są prostsze w użyciu, lepiej się synchronizują i prościej poczekać na zakończenie jakiegoś zadania, oraz co najważniejsze - task poprzez PPL (Parallel Programming Library , z kolei w C# jest TPL - Task Parallel Library), jest korelowany z wątkiem i to PPL zarządza osobnym wątkiem i dba, żeby było ok, do tego stopnia, że nie trzeba martwić się o ilość wątków, jakieś thread poolery etc. gdyż to wszystko robi za nas PPL- dla przykładu tworząc ręcznie wątki można stworzyć ich tyle, że program nie będzie nic robił tylko zmieniał konteksty na procesorze, natomiast używając PPL - wątków jest mniej niż tasków, niemniej to PPL tym zarządza by wszystko wykonywało się równolegle. Uważam, że do Twojego problemu lepszy będzie TTask niż twardy wątek, no i bardziej nowocześnie i łatwo.

Masz sznurek: http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_TTask_from_the_Parallel_Programming_Library

Przepiszę przykład

Kopiuj
procedure TForm1.Button1Click(Sender: TObject);
var
 aTask: ITask;
begin
 aTask := TTask.Create (procedure ()
   begin
     sleep (3000); // 3 seconds
     ShowMessage ('Hello');
   end);
 aTask.Start;
end;

Tutaj po kliknięciu przycisku metoda szybko się skończy i program będzie odblokowany, a po 3 sekundach nagle wyskoczy messagebox. Tutaj definiujesz podczas założenia taska procedurę, która się wykona. Coś takiego samego możesz osadzić w ShowForm, żeby asynchronicznie zacząć wczytywać pewne dane.

edytowany 1x, ostatnio: flowCRANE
MY
  • Rejestracja:ponad 9 lat
  • Ostatnio:11 dni
  • Postów:1082
0
furious programming napisał(a):

No i w czym to niby pomoże? Nadal okno będzie zamrożone i do czasu przetrawienia danych nie będzie odpowiadało na komunikaty. Bo tutaj problemem nie jest moment przetwarzania danych, a wydzielenie tego procesu z głównego wątku, tak aby interfejs nie ”wisiał”.

U siebie w programie jak miałem podobną sytuację, to tez stworzyłem własne AfterShow dokładnie w taki sposób jak kolega chciał. Po prostu takie było wymaganie, że najpierw ma się pokazać całe okno główne. Wtedy dopiero ma zacząć się ładowanie danych.

Co do wydzielenia tworzenia danych do oddzielnego wątku, to dyskutowałbym czy zawsze istnieje taka potrzeba. Jaki jest tego sens w tym przypadku skoro i tak z programu nie da się skorzystać w tym momencie, a dane muszą być przetworzone? Natomiast przerwanie ładowania danych można osiągnąć tak samo bez tworzenia nowego wątku.

somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
2

Ładowanie asynchroniczne jest sensowne wtedy kiedy można korzystać z innych funkcji programu nie wymagających danych. Dodatkowo jest lepsze UX, jeśli aplikacja coś robi i nie zamarza na ekranie, bez możliwości odrysowania, czy przesunięcia okna.

KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0

Jeszcze jedno pytanie, czy w Delphi poszczególne taski korzystają z kopii zmiennych lokalnych, czy też będą sobie te zmienne wzajemnie nadpisywać?
Pytam, ponieważ narobiłem ręcznie kopii zmiennych dla każdego taska, a syn piszący w C powiedział że to raczej bez sensu, że w C każdy nowy task operuje na swojej kopii zmiennych.

somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
0

W C nie ma taskow ;) Może chodzi o C#? Pokaz kawałek kodu.

KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0
somedev napisał(a):

W C nie ma taskow ;) Może chodzi o C#? Pokaz kawałek kodu.

Nie wiem, w jakim C on pisze, teraz jest na uczelni, jak wróci to spytam dokładniej.
Po prostu wychodząc rano z domu zwrócił mi uwagę, on nie zna Delphi więc pytam na forum.

somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
0

No to pokaz kawałek kodu bo nie wiem za bardzo o jakie zmienne i gdzie się Tobie rozchodzi (Twój kod)

KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0

Chodziło mi o zmienne lokalne procedury, np:

Kopiuj
procedure RobCos(var param:byte);
var task:array of ITask;
    zmienna:byte;

begin
   zmienna:=5;
   SetLength(task,zmienna);
   task[0]:=TTask.Create(procedure() begin
     zmienna:=10;
   end);
   task[0].Start;
   task[1]:=TTask.Create(procedure() begin
     zmienna:=20;
   end);
   task[1].Start;
   param:=zmienna;
end;

czy każdy task ma własną kopię zmiennej i na koniec tasku zmienna pozostaje niezmieniona , czy nawzajem sobie tą zmienną nadpisują.
Sądzę że sobie nadpisują, bo już kompilator żądał aby liczniki pętli były zmiennymi lokalnymi wewnątrz tasków.

Kopiuj
  var i:byte
begin
   task[1]:=TTask.Create(procedure() begin
       for i:=0 to 10 do

było źle, a poprawnie było:

Kopiuj
begin
   task[1]:=TTask.Create(procedure() var i:byte
     begin
       for i:=0 to 10 do

wobec czego każdy task u mnie używa tylko swoich lokalnych zmiennych.

edytowany 2x, ostatnio: Klakierus
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Tuchów
  • Postów:12164
1
somedev napisał(a):

To że @furious programming mówił o wątkach to sądzę, ze albo jest skrótem myślowym, albo po prostu nadal z tego korzysta, niemniej tutaj nie używał bym wątków, jako takich ale samego konceptu.

Miałem na myśli to, aby ładowanie danych przenieść do wątku pobocznego. A to czy skorzystamy z niego pośrednio czy bezpośrednio, zależy od tego jakie klasy mamy dostępne. Można użyć klasy TThread, można TTask – jak kto woli/umie. Byle tego użyć. ;)


Mr.YaHooo napisał(a):

U siebie w programie jak miałem podobną sytuację, to tez stworzyłem własne AfterShow dokładnie w taki sposób jak kolega chciał. Po prostu takie było wymaganie, że najpierw ma się pokazać całe okno główne. Wtedy dopiero ma zacząć się ładowanie danych.

I tak samo miałem w swoim platformerze. On jest aplikacją stricte jednowątkową, więc gdy odpalana jest pętla główna (wykonująca kod gry), to okno musi już być widoczne na ekranie, a OnShow wywoływane jest zanim okno fizycznie się pojawi. W Delphi działającym rozwiązaniem jest właśnie AfterShow, a w Lazarusie można to zrobić prościej – wymusić pokazanie okna w OnShow i tak mam teraz:

Kopiuj
procedure TGameForm.FormShow(ASender: TObject);
begin
  ShowWindow(Handle, SW_SHOWNORMAL); // tu wywalamy formularz na ekran
  Game.Start(); // tu startujemy główną pętlę gry
end;

Jeśli zaremuję linijkę z ShowWindow to gra będzie działać normalnie, ale okno nigdy się nie pojawi. Natomiast wołanie ProcessMessages po przetworzeniu każdej klatki gry pozwala wykonywać pętlę komunikatów okna na bieżący, czyli odmalowywać okno i je przesuwać/rozciągać.

No i tak – tutaj możemy z tego skorzystać, bo takie jest założenie. Jeśli założenie jest takie, że dany kod ma być wykonany tuż po tym jak okno pojawi się na ekranie to możemy skorzystać z AfterShow lub z tego co podałem dla innego IDE. Jeżeli natomiast kod ma się wykonywać zupełnie niezależnie to tylko wątki poboczne.

Co do wydzielenia tworzenia danych do oddzielnego wątku, to dyskutowałbym czy zawsze istnieje taka potrzeba.

Nie zawsze istnieje – mój platformer jest jednym z takich przypadków, gdy wątki nie są konieczne. Podobnych przypadków jest masa, jednak nie jest nim przypadek opisany w tym wątku. Chyba że jego autor nie zamierza zadbać o to, aby splash reagował na komunikaty (o istotności ich przetwarzania niżej).

Jaki jest tego sens w tym przypadku skoro i tak z programu nie da się skorzystać w tym momencie, a dane muszą być przetworzone?

Taki, że procedura obsługi komunikatów okna nie jest wstrzymywana podczas ładowania danych. A to oznacza, że dopóki dane są wczytywane, dopóty okno nie reaguje na żadne akcje. Nie da się go przesunąć, nie jest przemalowywane, nie reaguje na komunikaty myszy i klawiatury, ani na żadne inne. W przypadku splasha, najbardziej istotne jest odmalowywanie okna – które nie jest obowiązkowe, jednak wpływa na wrażenia estetyczne.

Przykładem poprawnej implementacji ekranu wizytówki jest GIMP. Pierwszy jego rozruch na moim laptopie trwa około pół minuty. W tym czasie ładowane są dane, a w splashu zmienia się treść etykiety i stan progressbara. Aby nie gapić się na splasha jak w święty obrazek, w miedzyczasie otwieram sobie katalogi czy robię coś w innych programach. Następnie minimalizuję wszystko i wracam do splasha GIMP-a. Ten ładnie się odmalowuje, kończy ładowanie i uruchamia edytor. Wszystko wygląda poprawnie, estetycznie.

Natomiast przykładem niepoprawnej implementacji splasha jest… Lazarus. Ten uruchamia się w kilka sekund, jednak ładowanie danych zrealizowane jest w taki sposób, o jakim napisałeś @flasher86 i Ty (rozwiązanie à la AfterShow) – czyli bez jakiejkolwiek synchronizacji. Okno splasha nie reaguje na żadne komunikaty i nie przemalowuje swojej powierzchni, więc jeśli przesunę nad nim okno, to wyryję w nim dziurę (która nigdy nie zostanie z powrotem zamalowana). A wygląda to paskudnie:

splash fail.png

Przy czym różnica pomiędzy splashami programów GIMP i Lazarus jest taka, że wizytówka tego pierwszego zawiera komponenty o zmiennej zawartości (etykiety z bieżącą operacją oraz progressbar), a tego drugiego jest zwykłą grafiką o statycznej zawartości. Tak więc splash programu GIMP musi być przemalowywany na bieżąco (w przeciwnym razie wyświetlanie informacji o postępie nie będzie miało sensu), a wizytówka Lazarusa nie musi (choć powinna, ze względu na estetykę).

Co prawda da się napisać kod ładujący w głównym wątku, w taki sposób, aby nie blokować kolejki komunikatów okna wizytówki, ale również zależy to od konkretnego przypadku. Bo inaczej można podejść do tego tematu, jeśli ładowanie danych trwa np. minutę i składa się z kilku wywołań długotrwale działających metod (np. po 10+ sekund na każde wywołanie), a inaczej, gdy trwa tyle samo, ale składa się z wywołań setek metod pracujących krótkotrwale (np. po góra 100ms na każde wywołanie).


Klakierus napisał(a):

czy każdy task ma własną kopię zmiennej i na koniec tasku zmienna pozostaje niezmieniona , czy nawzajem sobie tą zmienną nadpisują.

Przejdź się do dokumentacji/wiki i poczytaj o metodach anonimowych. ;)


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 10x, ostatnio: flowCRANE
KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0

Aby przyśpieszyć uruchamianie programu, chciałem przetwarzać kilka obrazów jednocześnie. Używam Win7 64bit, program podczas uruchamiania zużywa 10-15% czasu procesora i 60MB pamięci.
Niestety stworzenie więcej niż jednego wątku powoduje błąd "Out of resources" gdy Firefox zajmuje 3 procesy i zużywa łącznie ponad 500MB pamięci.
Jak zwiększyć dostępność pamięci dla programu? W kompilatorze znalazłem tylko wielkość stosu.

somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
0

Można użyć zmiennej z danej procedury wywołującej taska i wtedy wszystkie taski będą miały tą samą zmienną. Jeśli z kolei zadeklarujesz zmienną w samym tasku to jest ona w każdym inna, zresztą to są metody anonimowe czyli to są zupełnie dwie inne metody mimo, że mają podobne ciało. Możesz wywołać w środku zewn. procedurę, ale wtedy też w obrębie jej samej będzie iny stos. Jeśli użyjesz zmiennej z procedury która wywołuje task, to oczywiście ją zmienisz, nie jest ona kopiowana w obrębie taska. Zresztą w c# jest z tego co pamiętam podobnie. Zobacz efekt tego kodu:

Kopiuj
procedure TForm1.Button1Click(Sender: TObject);
var task:array of ITask;
    zmienna:byte;
begin
   zmienna:=5;
   SetLength(task,zmienna);

   task[0]:=TTask.Create(procedure()
    var liczba : byte;
    begin
     liczba := 2;
     zmienna:=zmienna + liczba;
   end);
   task[0].Start;

   task[1]:=TTask.Create(procedure()
    var liczba : byte;
    begin
     liczba := 3;
     zmienna:= zmienna + liczba;
   end);
   task[1].Start;

   task[0].Wait();
   task[1].Wait();

   ShowMessage(zmienna.ToString());

end; 
edytowany 1x, ostatnio: flowCRANE
flowCRANE
Przypominam o kolorowaniu kodu. :P
somedev
Odpowiadając na temat w danym dziale ten przyrostek pascal powinien automatycznie się wstawiać w zależności od działu bo zazwyczaj wklejam kod i klikam przycisk "kod" ;) Niemniej postaram się pamiętać.
flowCRANE
Lepiej tego nie implementować. W tym dziale wątki zawierają skrawki kodu napisane również w innych językach, np. w HTML czy C. Dlatego każdy wybiera takie kolorowanie jakie mu pasuje.
somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
0
Klakierus napisał(a):

Aby przyśpieszyć uruchamianie programu, chciałem przetwarzać kilka obrazów jednocześnie. Używam Win7 64bit, program podczas uruchamiania zużywa 10-15% czasu procesora i 60MB pamięci.
Niestety stworzenie więcej niż jednego wątku powoduje błąd "Out of resources" gdy Firefox zajmuje 3 procesy i zużywa łącznie ponad 500MB pamięci.
Jak zwiększyć dostępność pamięci dla programu? W kompilatorze znalazłem tylko wielkość stosu.

Pokaż kod - może gdzieś tworzysz w pętli w cholerę wątków i nie zdajesz sobie sprawy, że przeginasz. Dodatkowo jaki masz komputer? Jakie jest jego wykorzystanie przy komunikacie?

KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0

Jestem pewien że nigdzie nie tworzę wątków w pętli. Kod jest zbyt duży żeby go przedstawiać.
Albo mam gdzieś jakiś przeciek, albo to darmowe Delphi 10.3 jest niezbyt tego. Wcześniej program się bardzo dziwnie zachowywał, nie będę opisywać wszystkiego, tylko 2 przykłady. Zanim wstawiłem splash screen był tylko Label ukrywany po uruchomieniu, w założeniu, bo czasem się pojawiał, a czasem nie. Po zmianie w trakcie uruchamiania na visible:=false a po uruchomieniu visible:=true, działał zgodnie z zapisem, po przywróceniu (uruchamianie - true, po uruchomieniu - false) znowu raz się pojawiał a raz nie.
Przy wątkach czasami nie wychodził z wątka, a po 10 minutach debugowania dawałem spokój. Założyłem zmienną stan:byte i każdy z wątków ustawiał po zakończeniu odpowiednie bity na zmiennej stan. Przy debugowaniu się zdziwiłem, gdy pierwsza linia w procedurze "stan:=0" zarówno przed jej wykonaniem jak i po jej przejściu debuger twierdził że wartość jest niedostępna. Przerwałem na parę godzin. Zacząłem od początku (tworzenie wątków) i jak na razie działa. Czas uruchamiania poprawił się do 5-6 sekund, a wykorzystanie procesora wzrosło do ponad 60% przy uruchamianiu. Szukałem przecieków, lecz nic nie znalazłem.
Wcześniej wymieniony Label dalej działał jak chciał, więc za radą @furious programming zrobiłem splasha. Wszystko było dobrze oprócz tego, że miałem na pasku 2 ikony programu (jedna wskazywała na program, druga na pustą aplikację). Zamknięcie którejkolwiek z ikon zamykało program.
Zmieniłem delphi Application.MainFormOnTaskbar := False; i wygląda wszystko dobrze (na razie).

edytowany 2x, ostatnio: Klakierus
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Tuchów
  • Postów:12164
0
Klakierus napisał(a):

Wcześniej wymieniony Label dalej działał jak chciał, więc za radą @furious programming zrobiłem splasha. Wszystko było dobrze oprócz tego, że miałem na pasku 2 ikony programu (jedna wskazywała na program, druga na pustą aplikację).

Obstawiam, że nie ustaliłeś poprawnie Ownera w konstruktorze tego okna.


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.
KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0
Kopiuj
begin
  Application.Initialize;
  Screen:=TScreen.Create(Application);
  Screen.Show;
  Screen.Update;
  Application.MainFormOnTaskbar := False;
  Application.CreateForm(TForm1, Form1);
  Form1.Przygotowanie;
  Application.CreateForm(TKonfig, Konfig);
  Application.CreateForm(TPodgWydr, PodgWydr);
  Application.CreateForm(TWczytQR, WczytQR);
  Screen.Close;
  Screen.Free;
//  Screen.Destroy;
  Application.Run;
end.
MY
  • Rejestracja:ponad 9 lat
  • Ostatnio:11 dni
  • Postów:1082
0
furious programming napisał(a):

I tak samo miałem w swoim platformerze. On jest aplikacją stricte jednowątkową, więc gdy odpalana jest pętla główna (wykonująca kod gry), to okno musi już być widoczne na ekranie, a OnShow wywoływane jest zanim okno fizycznie się pojawi. W Delphi działającym rozwiązaniem jest właśnie AfterShow, a w Lazarusie można to zrobić prościej – wymusić pokazanie okna w OnShow i tak mam teraz:

O ile dobrze kojarzę, to w C++ Bilderze też to tak działa. Ja wybrałem sposób z wysłaniem komunikatu oraz obsłużeniem go za pomocą mechanizmu BEGIN_MESSAGE_MAP END_MESSAGE_MAP po prosto tak według mnie jest bardziej elegancko.

furious programming napisał(a):

Nie zawsze istnieje – mój platformer jest jednym z takich przypadków, gdy wątki nie są konieczne. Podobnych przypadków jest masa, jednak nie jest nim przypadek opisany w tym wątku. Chyba że jego autor nie zamierza zadbać o to, aby splash reagował na komunikaty (o istotności ich przetwarzania niżej).

Według mnie dobrze zaprojektowana aplikacja oprócz pokazania samego splasha powinna pokazywać na nim również jakiś progressbar żeby było faktycznie wiadomo, że coś się dzieje. Jednak jeśli mamy do czynienia z wykonywaniem w wątku głównym ładowania całego programu też przecież jest możliwość aby całość ładnie reagowała na komunikaty.

Taki, że procedura obsługi komunikatów okna nie jest wstrzymywana podczas ładowania danych. A to oznacza, że dopóki dane są wczytywane, dopóty okno nie reaguje na żadne akcje. Nie da się go przesunąć, nie jest przemalowywane, nie reaguje na komunikaty myszy i klawiatury, ani na żadne inne. W przypadku splasha, najbardziej istotne jest odmalowywanie okna – które nie jest obowiązkowe, jednak wpływa na wrażenia estetyczne.

A co za problem podczas aktualizacji postępu w progressbarze dać ProcessMessagess? Wtedy ładnie wszystko działa. Tak wiem, jest to dość brzydki sposób i wiele osób go krytykuje. Jednak czasami warto sięgnąć po taki sposób, szczególnie jeśli ktoś jest niedoświadczony w pracy z wątkami i potem w pewnym momencie coś mu wybuchnie..

Co prawda da się napisać kod ładujący w głównym wątku, w taki sposób, aby nie blokować kolejki komunikatów okna wizytówki, ale również zależy to od konkretnego przypadku. Bo inaczej można podejść do tego tematu, jeśli ładowanie danych trwa np. minutę i składa się z kilku wywołań długotrwale działających metod (np. po 10+ sekund na każde wywołanie), a inaczej, gdy trwa tyle samo, ale składa się z wywołań setek metod pracujących krótkotrwale (np. po góra 100ms na każde wywołanie).

O i tu mamy decydującą kwestię. Jeśli ja u siebie mam dużo krótkich wywołań, wtedy to wszystko ma sens. ProgressBar idzie gładko do przodu. Jeśli mam mało wywołań które trwają wieki, takie coś jest bez sensu. Bo i tak wtedy trzeba odczekać te parę (czasem paręnaście) sekund nim kolejny krok dobiegnie końca i zostanie wywołane magiczne ProcessMessages

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Tuchów
  • Postów:12164
0

@Klakierus: utwórz okno splasha podając w konstruktorze nil – spróbuj w ten sposób.


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 2x, ostatnio: flowCRANE
KL
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 4 lata
  • Postów:92
0
furious programming napisał(a):

@Klakierus: utwórz okno splasha podając w konstruktorze nil – spróbuj w ten sposób.

Gdy dawałem nil to wcześniej nie zamykał się splash po zamknięciu programu. Teraz nie ma różnicy.
Jeśli użyję Application.MainFormOnTaskbar := True; to pozostaje jedna ikona po zamknięciu programu.

Program na razie działa tak jak chciałem. Dziękuję wszystkim za pomoc.

edytowany 8x, ostatnio: flowCRANE
flowCRANE
Nie mam Delphi więc nie sprawdzę. Poczekamy na opinie użytkowników tego środowiska.
PW
@furious programming zainstaluj sobie Delphi Community Edition, czemu się tak przed tym bronisz ?
KL
Używam tej darmówki (Delphi Community Edition) od 2 tygodni i mam pytanie czy u was ona dobrze działa? U mnie troszkę denerwuje (zamierzam powrócić do poprzedniego RAD 10 Seatle). Czasem w trakcie pisania cała strona robi się czerwona od błędów, po Ctrl+F9 jest znowu OK, czasem uparcie samoczynnie wstawia "end;" w środku procedury (przy jej edycji) czasem w lewym górnym oknie (structure) wywala masę błędów, które przy kompilacji znikają. Ostatni program, a pierwszy pisany w tej darmówce też mnie czasem dziwi, lecz przypisuje to możliwym moim błędom.
flowCRANE
@PrzemysławWiśniewski: nie mam zwyczaju instalowania oprogramowania (i dodatkowego rejestrowania), którego i tak nie będę używać. A nie będę tego robić tylko po to, aby móc służyć innym. No i nie po to lata temu przesiadłem się z Delphi na Lazarusa, aby teraz wracać na stare śmieci.
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)