Wątek odliczający przetworzone rekordy

Wątek odliczający przetworzone rekordy
BU
  • Rejestracja:ponad 22 lata
  • Ostatnio:minuta
  • Postów:296
0

Pomóżcie, bo nie mam kompletnie pojęcia jak się za to zabrać.
Mam aplikację, która pobiera plik XML z sieci i następnie go przetwarza. Rekordów w tym pliku jest kilkadziesiąt tysięcy. Oczywiście podczas przetwarzania, aplikacja "wisi". Chciałbym zrobić licznik, który wyświetlałby, który aktualnie rekord jest przetwarzany. Wiem, że muszę zastosować wątki. Przewertowałem kilka stron internetowych na ten temat i przyznam szczerze, że nie jestem, ani odrobinę mądrzejszy. Czy jakaś dobra dusza poratowałaby receptą na moje schorzenie???
Z góry bardzo dziękuję.


Pozdrawiam
Buster
obscurity
  • Rejestracja:około 6 lat
  • Ostatnio:około 24 godziny
1

Szczerze jeśli przerobiłeś kilka stron i dalej nie rozumiesz to ciężko podjąć wyzwanie żeby to wytłumaczyć lepiej.
Najprostszym rozwiązaniem jest wywołanie Application.ProcessMessages po każdym rekordzie, wtedy aplikacja się na chwilę odwiesi i odświeży interfejs żeby pokazać aktualny rekord. To nie jest idealne rozwiązanie bo aplikacja nadal będzie w większości wisieć i nie będzie odpowiadać ale może ci to wystarczy.

Delphi z tego co wiem nie ma łatwego modelu wielowątkowości, więc jeśli chcesz to rozwiązać lepiej to albo musisz poszukać jakiejś biblioteki która w tym pomoże albo usiąść i zrozumieć wątki, z tym że czeka na ciebie pewnie parę pułapek na początku.


"A car won't take your job, another horse driving a car will." - Horse influencer, 1910
edytowany 1x, ostatnio: obscurity
KA
Czego Delphi nie ma? Od 2000 roku albo i wcześniej Delphi ma tak podstawową klasę jak TThread a na jego potrzeby z jej pomocą to coś co może zrobić nawet początkujący...
obscurity
TThread to trochę armata do czegoś takiego, nie ma wbudowanej obsługi await/async, coroutines ani green threads, nie wiem czy jest ThreadPool, praca z TThread jest nieprzyjemna i nieintuicyjna dla początkujących
KA
A co tam ci trzeba TThread i metoda Synchronize tu do tego nic nie trzeba. Oczywiście Delphi ma wiele bardziej zaawansowanych mechanizmów ale to o czym wspominasz to właśnie są armaty a TThread to taka podstawa do tego, wystarczy ręcznie napisać kilka linijek kodu i sprawa załatwiona. Dema niektórych możliwości https://github.com/djjd47130/DelphiThreadDemo/tree/master
obscurity
to armata w sensie zasobożerności, wiem czym jest TThread i jak się go używa. Jeśli chcesz to wytłumacz to autorowi wątku który twierdzi że przeczytał kilka artykułów i nadal tego nie rozumie. Sam pamiętam gdy raczkowałem w programowaniu i temat wątków był dla mnie nowy, miałem zwłaszcza problemy z synchonizacją, atomowością operacji, przekazywaniem danych do wewnątrz wątku i odczytywaniem rezultatu. W sumie tu niewiele trzeba i masz rację że nie jest to skomplikowane jak się załapie, ale w innych językach jest to po prostu lepiej rozwiązane i łatwiejsze.
MY
  • Rejestracja:ponad 9 lat
  • Ostatnio:3 dni
  • Postów:1082
0

Ale problem jest w tym, że aplikacja pobierając plik zamarza, czy podczas przetwarzania zawiesza się?

Nie mniej jednak jedno i drugie da się rozwiązać. Nie wiem jak pobierasz plik, ale dodanie progressbar'a podczas pobierania pliku będzie fajnym rozwiązaniem, do tego łatwym w zaimplementowaniu. Tak samo można pobrać liczbę rekordów w pliku i wyświetlić postęp za pomocą kolejnego albo tego samego progress'bara. Przy czym jak @obscurity zauważył potrzeba po ustawieniu postępu dać Application.ProcessMessages inaczej Window's nie będzie odświeżał okna.

Do takiego problemu moim zdaniem nie ma co ładować wątków. I tak program nie będzie w stanie niczego innego zrobić.

Marius.Maximus
  • Rejestracja:ponad 14 lat
  • Ostatnio:około 8 godzin
  • Postów:2070
1

coś mi świta że działał pasek postępu działał mi kiedyś w taki sposób

Kopiuj
function DevChannel.DownloadID(SourceFile, DestFile: string): Boolean;
var
  Http: TIdHTTP;
  MS: TMemoryStream;
begin
  Http := TIdHTTP.Create(nil);
  try
    MS := TMemoryStream.Create;
    try
      Http.OnWork:= HttpWork;

      Http.Get(SourceFile, MS);
      MS.SaveToFile(DestFile);

    finally
      MS.Free;
    end;
  finally
    Http.Free;
    pb1.Position := 0;
  end;
  Result := True;
end;

procedure DevChannel.HttpWork(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Int64);
var
  Http: TIdHTTP;
  ContentLength: Int64;
  Percent: Integer;
begin
  Http := TIdHTTP(ASender);
  ContentLength := Http.Response.ContentLength;

  if (Pos('chunked', LowerCase(Http.Response.ToString)) = 0) and
     (ContentLength > 0) then
  begin
    Percent := 100*AWorkCount div ContentLength;
    pb1.Position := Percent;
  end;
end;

teraz to był zrobił wątek i wysyłał do głównego wątku wiadomości o postępie prac, bo takie używanie Application.ProcessMessages w długo trwającej funkcji też może być problemem a przynajmniej na Linux+Lazarus


--
Nie przyjmuję reklamacji za moje rady, używasz na własną odpowiedzialność.
Programowanie bez formatowania to jak chodzenie ze spodniami spuszczonymi na kostki. Owszem da się ale po pierwsze nie wygodne, po drugie nieprzyzwoicie wygląda.
Przed zaczęciem nowego wątku przeczytam problem XY
edytowany 1x, ostatnio: Marius.Maximus
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Tuchów
  • Postów:12166
3

Przecież do tego wystarczy prosty TThread — do tego celu istnieje. Odpal sobie cały proces w wątku, a w Synchronize odświeżaj interfejs. Ta klasa jest tak prosta w obsłudze, że nawet nie ma co się za bardzo rozpisywać.

Application.ProcessMessages od biedy można też użyć, ale działa to dobrze tylko wtedy, gdy wywołuje się tę metodę w bardzo krótkich odstępach czasu (aby maksymalnie ukryć zamrażanie się interfejsu). Wątek poboczny jest zdecydowanie lepszy, bo interfejs w ogóle nie zamarza, a wszystko co musi być w twoim przypadku synchronizowane, to jedynie aktualizacja interfejsu, co i tak jest banalne.

W sieci znajdziesz masę przykładów jak używać TThread. Natomiast w Kompendium jest przykładowa aplikacja, której zadaniem jest przeszukiwać dysk w wielu wątkach (po jednym wątku na partycję) i wyświetlać wyniki w komponencie. Pobaw się w coś takiego, tylko o synchronizacji nie zapomnij (Adam zapomniał).


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 5x, ostatnio: flowCRANE
KA
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Gorlice
1

Jeżeli nie masz zabytkowego Delphi to chyba prościej już się nie da:

Kopiuj
unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls, System.Threading;

type
  TForm2 = class(TForm)
    btnStart: TButton;
    ProgressBar1: TProgressBar;
    btnCancel: TButton;
    procedure btnStartClick(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    { Private declarations }

    aTask: ITask;
    fCancel: Boolean;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}


procedure TForm2.btnCancelClick(Sender: TObject);
begin
  if (Assigned(ATask)) then
  begin
    fCancel:= True;
    aTask.Cancel;
    aTask:= nil;
  end;
end;

procedure TForm2.btnStartClick(Sender: TObject);
begin
  if Assigned(ATask) and (Atask.Status = TTaskStatus.Running) then
    exit;

  fCancel:= False;

  //początkowe ustawienia paska postępu
  ProgressBar1.Min:= 0;
  ProgressBar1.Max:= 100;
  ProgressBar1.Position:= 0;

  var totalCount:= 1000; //ile zadań do zrobienia
  var percent:= 0;  //na poczatkek init z 0 postępu

  aTask:= TTask.Create(
  procedure
  begin
    for var i:= 1 to totalCount do
    begin
       if fCancel then
         break;

       Sleep(1000); // symuulacja czasochłonnej operacji

       TThread.Synchronize(TThread.Current,
       procedure
       begin
         if not fCancel then
         begin
           percent:= Trunc((i / totalCount) * 100); //obliczanie procent
           ProgressBar1.Position:= percent;  //wyświretl postęp
           Form2.Caption:= Format('Wykonano %0:d z %1:d [%2:d%%]', [i, totalCount, percent]);
         end
         else
           Form2.Caption:= 'Anulowano';
       end);
     end;
  end);
  aTask.Start;
end;

procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  try
    if (Assigned(ATask)) then
    begin
      fCancel:= True;
      aTask.Cancel;
      aTask:= nil;
    end;
  except
    on E: EOperationCancelled do
      CanClose:= True;
  end;
  CanClose:= True;
end;

end.

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.
WL
  • Rejestracja:około 21 lat
  • Ostatnio:około 2 miesiące
  • Postów:1082
2
Buster napisał(a):

Pomóżcie, bo nie mam kompletnie pojęcia jak się za to zabrać.

Najpierw zdefiniuj problem.

A IMO to mogą być (luba nawet są) dwa odrębne problemy.

  1. Przetwarzanie pliku XML
  2. Przetwarzanie danych z XML dalej - nie wiem co robisz z tymi danymi z XML, być może coś tam robisz może w bazie.

No, ale dano Ci już odpowiedź o wątkach albo nawet o pobieraniu plików, a nawet pojawił się auto-magiczny Application.ProcessMessages🤦‍♂️
Chociaż na samą wizualizację "który rekord jest przetwarzany" to nie jest złe rozwiązanie.
Tylko, że to żadne rozwiązanie.

Jeśli problem dotyczy przetwarzania XML'a a używasz DOM, to nic z tym nie zrobisz.
Dopóki nie przejdziesz na przetwarzanie zdarzeniowe, czyli SAX.

Jeśli ciężar jest przesunięty na moment przetwarzania odczytanych danych z XML (nie wiem co robisz, może import do bazy?) to pewnie pula tzw. worker-threads będzie rozwiązaniem.
Ale jak nie wiadomo dokładnie z czym masz problem, to jak Ci pomóc?

Coś mi pachnie, że problem dotyczy obu obszarów - tj. samo prasowanie i odczyt danych z XML i przetwarzanie danych z XML.

edytowany 1x, ostatnio: wloochacz
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)