Aplikacja wielowątkowa lub inne rozwiązanie

0

Witam !

Mam problem z wzajemnym wywoływaniem się procedur.

W poniższym przykładzie są 2 przyciski i 2 Labele, Procedura Button2 zlicza licznikiem Licznik2 do 40 ale zajmuje mu to 4 sek, Button1 zlicza Licznikiem1 ale wywołuje również click-nięcie Buttona2, przez co musi czekać aż miną te 4 sekundy zanim doda do licznika1.

Teraz pytania:

  • czy jest jakiś sposób by wywołana procedura robiła się w tle, lub by program Buttona1 nie czekał aż się skończy wykonywać procedura Buttona2 ?(czytałem na forum coś o wątkach ale sprawa wygląda dość skomplikowanie, może jest jakiś prostszy sposób)
  • zakładając ze pierwszy problem uda się rozwiązać, czy da się zrobić tak by procedura Buttona1 nie wywoływała ponownie procedury Buttona2 dopóki się ona nie zakończy ale robiła swoje czyli dodawała do licznika1, a jak się procedura Button2 skoczy to znowu ja wywoływała ?

Poniżej kod przykładu, przykład jest tylko zobrazowaniem problemu wiec proszę się nie skupiać nad przyciskami, Labelami czy innymi szczegółami.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

  Licznik1, Licznik2 : Integer;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Button2.Click;
  Licznik1 := Licznik1 +1;
  Label1.Caption := InttoStr(Licznik1)
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  a : Integer;
begin
  for a :=1 to 40 do
  begin
    Licznik2 := Licznik2 +1;
    Label2.Caption := InttoStr(Licznik2);
    sleep(100);
    Application.ProcessMessages;
    if Application.Terminated then Break
  end;
end;

end.

Pozdrawiam Szymon

1

Button2 powinien tworzyć wątek. O wątkach poczytaj Wątki a drugi problem to prosta sprawa użyj jakieś zmiennej i sprawdzaj jej stan czyli np if Active = False then Button2.Click;

0

Button2 powinien tworzyć wątek.

Czyli: Jak przekombinować coś banalnego.

Jest tylko jeden wypadek gdy musisz użyć wątków: Twoja procedura nie wraca a nie masz możliwości jej zmiany.

Podany kod można przebudować tak aby nie wymagał wątków poprzez mądre zarządzanie powrotami oraz bez użycia sleep który właśnie powoduje że ta procedura nie wraca.
Jeżeli pytacz by powiedział co chce osiągnąć w rzeczywistości, to może znalazłbym jakieś konkretne rozwiązanie, bo na tak wymyślnych nazwach jak Button1 czy Licznik1 bardzo łatwo się nie myśli.

Widzę @kAzek ,że jak ja napisałem klasę serwera na Synapse w 100 liniach z metodą LoopStep którą się wykonywało 'co łaska' z głównego loopa i która robiła potrzebne rzeczy w stylu przyjmowanie klientów etc., tak ty byś to napisał w 200 liniach z wątkiem (a może oddzielny na dodawanie, rozłączanie klientów, odbieranie i wysyłanie?) oraz z masą blokad i innych porąbanych rzeczy które obniżałyby wydajność na rzecz bardziej skomplikowanego rozwiązania.

0
szymon-ek napisał(a):

W poniższym przykładzie są 2 przyciski i 2 Labele, Procedura Button2 zlicza licznikiem Licznik2 do 40 ale zajmuje mu to 4 sek, Button1 zlicza Licznikiem1 ale wywołuje również click-nięcie Buttona2, przez co musi czekać aż miną te 4 sekundy zanim doda do licznika1.

Teraz pytania:

  • czy jest jakiś sposób by wywołana procedura robiła się w tle, lub by program Buttona1 nie czekał aż się skończy wykonywać procedura Buttona2 ?(czytałem na forum coś o wątkach ale sprawa wygląda dość skomplikowanie, może jest jakiś prostszy sposób)
  • zakładając ze pierwszy problem uda się rozwiązać, czy da się zrobić tak by procedura Buttona1 nie wywoływała ponownie procedury Buttona2 dopóki się ona nie zakończy ale robiła swoje czyli dodawała do licznika1, a jak się procedura Button2 skoczy to znowu ja wywoływała ?
efgegs napisał(a):

Button2 powinien tworzyć wątek.

Czyli: Jak przekombinować coś banalnego.

Jest tylko jeden wypadek gdy musisz użyć wątków: Twoja procedura nie wraca a nie masz możliwości jej zmiany.

Podany kod można przebudować tak aby nie wymagał wątków poprzez mądre zarządzanie powrotami oraz bez użycia sleep który właśnie powoduje że ta procedura nie wraca.
Jeżeli pytacz by powiedział co chce osiągnąć w rzeczywistości, to może znalazłbym jakieś konkretne rozwiązanie, bo na tak wymyślnych nazwach jak Button1 czy Licznik1 bardzo łatwo się nie myśli.

To możne znasz inny sposób aby program jednocześnie wykonywał się w 2 miejscach? Ten program jest przykładem czytaj ze zrozumieniem.

Co do reszty szkoda odpisywać geniuszu.

0

Witam !

Dziękuje za szybka odpowiedź.

Co do programu, to trochę więcej informacji o realnym problemie dla efgegs żeby łatwiej się myślało ;-)

Button 2 to długa procedura robienia testów, zajmuje więcej niż 2 minuty, woła ze 30 innych procedur liczących, komunikacyjnych się po portach USB, RS232 i wszystkim co można sobie wymyślić. Ta procedura wywoływana jest buttonem2, i spoko , robię Enable := False i nie da się go drugi raz kliknąć, to samo mogę użyć do zapobiegania drugiemu uruchomieniu z zewnątrz. Procedura ma w sobie jakieś sleepy szczególnie w protokołach komunikacyjnych ale nie wiele się z nimi da zrobić, a nawet jeśli to strasznie dużo pracy by to zajęło i testów po tym.

Button1 to symulacja zdarzenia wywoływanego przez rozkaz z portu rs232, ma on symulować tak jak by użytkownik zdalnie naciskał Button2, ale nie morze czkać aż się zakończy Button2 bo w międzyczasie ten sam port jest wykorzystywany przez procedury z buttona2.

Tak wiec jak na razie spróbuje pokombinować w kierunku odpowiedzi kAzek i zobaczymy co się uda zrobić ;-)

Jeśli ktoś ma jakieś prostsze rozwiązania chętnie posłucham ;-)

Pozdrawiam Szymon

0

Jedź na wątkach, nie po to się ludzkość parę dekad męczyła by stworzyć systemy zaprojektowane pod pracę wielowątkową by się teraz uwsteczniać i robić wsadowo.

Pamiętaj tylko żeby użyć synchronizacji tam gdzie trzeba (głównie przy zmianach w GUI, ale też tam gdzie dwa wątki grzebią w jednej zmiennej).

0

To możne znasz inny sposób aby program jednocześnie wykonywał się w 2 miejscach?

Problem w tym że tutaj nie ma takiej potrzeby. Tylko ty jej szukasz.

Ten program jest przykładem czytaj ze zrozumieniem.

Jeżeli dla ciebie sleep jest dobrym przykładem funkcji blokującej, to gratuluje. To jest raczej tworzenie sztucznego problemu.

Co do reszty szkoda odpisywać geniuszu.

Owym 'geniuszem' (tj. domniemanym idiotą) jesteś Ty, mój 'drogi' rozmówco. Nie mam zamiaru pozwalać sobie dmuchać w kaszę. Ja oczekuję rozmowy na poziomie, bez twoich kryptowyzwisk.

Button 2 to długa procedura robienia testów, zajmuje więcej niż 2 minuty, woła ze 30 innych procedur liczących, komunikacyjnych się po portach USB, RS232 i wszystkim co można sobie wymyślić.

No to to jest przykład funkcji blokującej. Do wątku.
Mimo że zapewne można to zaprogramować bez wątków to tutaj wątki się przydadzą żeby nie tracić liniowości tego kodu.

morze

Srsly?!

ma on symulować tak jak by użytkownik zdalnie naciskał Button2, ale nie morze może czkać aż się zakończy Button2 bo w międzyczasie ten sam port jest wykorzystywany przez procedury z buttona2.

No tego to nie rozumiem. Chcesz mieć parę testów wykonywanych na raz? Jakieś 'naciskanie zdalne', wtf. Chcesz dwa razy wykonywać to co sam stwierdziłeś że nie możesz robić bo 'port jest w użyciu'. Jakaś magia.

nie po to się ludzkość parę dekad męczyła by stworzyć systemy zaprojektowane pod pracę wielowątkową by się teraz uwsteczniać i robić wsadowo.

Systemy zaprojektowane pod pracę wielowątkową wymagają blokad i innych pierdół.
W przypadku prostych zadań łatwiej jest wykorzystać wsadowość. Tak można robić gdy np. masz jedną dużą procedurę a reszta to jakieś mniejsze obsługi GUI, a jest to częsty wariant.

0
szymon-ek napisał(a):

Teraz pytania:

  • czy jest jakiś sposób by wywołana procedura robiła się w tle, lub by program Buttona1 nie czekał aż się skończy wykonywać procedura Buttona2 ?

No jasne że nie ma potrzeby a to jak miało być zrealizowane? Musi być wątek skoro coś ma się wykonywać w tle.

Co do owych jak to określiłeś "kryptowyzwisk" sam się pochwaliłeś jaki to świetny projekt zrealizowałeś tylko nie wiem kogo to obchodzi i co on ma wspólnego z tematem a tym bardziej skąd zgadywanki w jaki sposób ja bym go napisał więc to raczej wymagając pewnego poziomu rozmowy sam go zachowaj.

0

tylko nie wiem kogo to obchodzi i co on ma wspólnego z tematem

Z tematem ma tyle wspólnego że nie wszędzie gdzie trzeba robić parę rzeczy na raz potrzebne jest użycie wątków. Widać że masz problem ze zrozumieniem morału mojego przykładu.

a tym bardziej skąd zgadywanki w jaki sposób ja bym go napisał

Cóż, jak widać masz problem ze zrozumieniem mojego przykładu który ma na celu pokazanie że wszędzie (nawet tam gdzie to jest niepotrzebne) chcesz zastosować wątki.

Co do owych jak to określiłeś "kryptowyzwisk"

Nie odwołujesz się do tego pojęcia a jedynie usiłujesz uczepić się tego co pisałem.

raczej wymagając pewnego poziomu rozmowy sam go zachowaj.

Pokazuj mi jak nie zachowuje poziomu wypowiedzi waląc jedno superfajnedługiemiłeiprzyjemne zdanie które nawet ma kropkę na końcu ale żadnej interpunkcji, czy większego składu.

Musi być wątek skoro coś ma się wykonywać w tle.

Czyżbym nie mówił o wsadowości? Można wykonywać rzeczy w tle bez wątków jeżeli się umie, tylko że tutaj zakładam że się to nie opłaca bo ta procedura 'testująca' zapewne jest bardzo liniowa i ciężko by się ją rozbijało.
W ogóle to pojęcie 'w tle' to pojęcie bardzo zwodnicze, bo jest coś takiego jak task switching, i szansa ze akurat będą się wykonywać oba wątki twojego programu jest bardzo niska. Więc w ogóle mówienie o tym jest bez sensu, zwłaszcza że jeszcze są komputery z jednym rdzeniem. Więc wiele wątków jest zazwyczaj praktycznie tym samym co wsadowość tylko że należy pamiętać o blokadach i innych wtfach.

0

Widzę ze dyskusja z Tobą nie ma sensu bo obrastasz w piórka i wydaje Ci się, że wszystko wiesz najlepiej. Przypominam że do tej pory nie podałeś autorowi tematu lepszego (czyli poza użyciem wątku) sposobu zrealizowania tego aby kod, który w przykładzie jest pod przyciskiem button2 był wykonywany w tle nawet gdyby to była bardzo długo wykonująca się procedura a jednocześnie można było w dowolnym momencie kliknąć na przycisk button1 tak aby wykonał się jego kod. Jak już krytykujesz rób to konstruktywnie czekam na rozwiązanie.

3

grzeczniej, bo was porozsadzam

0

Widzę ze dyskusja z Tobą nie ma sensu bo obrastasz w piórka i wydaje Ci się, że wszystko wiesz najlepiej.

Widzę że nie jestem odosobniony w pleceniu przypuszczeń które to tak krytykujesz, hipokryto?

Przypominam że do tej pory nie podałeś autorowi tematu lepszego (czyli poza użyciem wątku) sposobu zrealizowania tego aby kod, który w przykładzie jest pod przyciskiem button2 był wykonywany w tle nawet gdyby to była bardzo długo wykonująca się procedura

I nigdy nie mówiłem że to zrobię. Doskonale wiem że jeżeli procedura jest blokująca to nie ma po co się bawić, bo można kombinować dla sportu, ale po co. Natomiast w przykładzie procedura blokująca sleep wyłącznie sztucznie blokuje.

Jak już krytykujesz rób to konstruktywnie czekam na rozwiązanie.

Krytykuję, bo w przykładzie wcale nie ma potrzeby używania wątku i ty doskonale o tym wiesz. Bah, można by napisać kod który spełniałby podane przez ciebie założenia, ale co z tego? Nie będę pisać kodu aby tobie coś pokazać bo nie jestem na tej fazie rozwoju. Można, ale to nie jest opłacalne gdy pytacz wyjaśnił po co w rzeczywistości mu ten kod i że jest on raczej blokujący (rzeczywistego kodu brak, więc brak realnej oceny), co wyraźnie powiedziałem, natomiast ty wciąż pytasz o pytania na które już odpowiedziałem. Wcześniej udzieliłem wyraźnych odpowiedzi na pytania które wciąż zadajesz. Zmień zestaw, bo na ten odpowiedziałem już parokrotnie. No chyba że twoje zarzuty kończą się na urojonych. Natomiast moje stanowisko jest jasne: Pierwszy kod nie był blokujący, można go przerobić bez wątków natomiast ty od razu odsyłasz każdego do wątków co jest bez sensu. Zapewne kod pytacza również nie wymagałby wątków gdyby był odpowiednio napisany. Ale zazwyczaj kod łatwiej jest robić liniowo, stąd te założenie blokowania.

grzeczniej, bo was porozsadzam

Cóż, na to można co najwyżej odpowiedzieć: Idź trzymaj waszą bazę danych żeby się (znowu) nie wysypała a nie bawisz się w szeryfa bez rewolwera.

0

Witam Ponownie !

Panowie po pierwsze się nie kłóćcie bo nie o to chodzi. Obu dziękuje za pomoc i odmienne punkty widzenia.

Co do mojej drogi to wybrałem wątki bo linki kAzek były proste łopatologiczne i czytelne, bez 100 linij kodu do zrobienia jednej rzeczy. Tak wiec kod wynikowy zamieszczam poniżej, działa ale miałem jeden problem, ale to tym później.

Co do sposobu efgegs, to jedyny komentarz jaki mi się nasuwa to to, by słowa poprzeć przykładami, samo rozpisywanie może coś daje zawodowcom, ale mnie zielonemu jak szczypiorek na wiosnę nie wiele mówi. Dlatego też zamieszczam kod wynikowy nie po to by się pochwalić, tylko by ułatwić życie innym użytkownikom tego forum bo nie ma jak to stare dobre CTRL C i CTRL V ;-))) Tak wiec przykłady na działanie bez wątków mile widziane.

Co do problemu, to odpaliłem kod poniżej i ni z gruch ni z pietruchy po naciśnięciu Buttona2 program doliczył do 5 i zwis czy co nie wiem bo button1 chodził ok. Dwie kompilacje potem znowu doliczył do 37 i koniec ???? Teraz nie potrafię już powtórzyć tego problemu, wiec nie wiem czy źle się skompilowało, błąd windy czy promieniowanie kosmiczne, niemniej teraz działa OK.

Obiecany kod:

 
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
type
  TTest = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;
 
var
  Form1: TForm1;
 
  Licznik1, Licznik2 : Integer;
 
implementation
 
{$R *.dfm}
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  if Button2.Enabled = True Then Button2.Click;
  Licznik1 := Licznik1 +1;
  Label1.Caption := InttoStr(Licznik1)
end;
 
procedure TForm1.Button2Click(Sender: TObject);
var
  Test : TTest;
begin
  Button2.Enabled := False;
  Test := TTest.Create(False);
end;
 
procedure TTest.Execute;
var
  a : Integer;
begin
  FreeOnTerminate := True; // zakoncz watek po zaknczeniu tej procedury
  for a :=1 to 40 do
  begin
    Licznik2 := Licznik2 +1;
    Form1.Label2.Caption := InttoStr(Licznik2);
    sleep(100);
    Application.ProcessMessages;
    if Application.Terminated then Break
  end;
  Form1.Button2.Enabled := True;
end;
 
end.
 

Pozdrawiam i dziękuje za pomoc

Szymon

0
type
  TTest = class(TThread)
  private
    { Private declarations }
    procedure EnableButton;
    procedure UpdateCaption;
  protected
    procedure Execute; override;
  end;

procedure TTest.UpdateCaption;
begin
  Form1.Label2.Caption := InttoStr(Licznik2);  
end;

procedure TTest.EnableButton;
begin
  Form1.Button2.Enabled := True;  
end;

procedure TTest.Execute;
var
  a : Integer;
begin
  FreeOnTerminate := True; // zakoncz watek po zaknczeniu tej procedury
  for a :=1 to 40 do
  begin
    Licznik2 := Licznik2 +1;
    Sleep(100); // WTF?!?!?!?
    Synchronize(@UpdateCaption);
  end;
  Synchronize(@EnableButton);
end;

Poczytaj o synchronizacji kodu. Jeśli zmiennej Licznik2 używasz tylko w wątku to przenieś ją jako pole klasy TTest. A jeśli jeszcze gdzieś indziej w kodzie to poczytaj o CriticalSection.

0

Patrząc na ten kod wydaje mi się że tu zupełnie nie są potrzebne wątki. Zamiast tagów można użyć trzy zmienne.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Button2.Enabled then Button2.Click;
  Label1.Tag:=Label1.Tag+1;
  Label1.Caption:=IntToStr(Label1.Tag);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Button2.Enabled:=false;
  Timer1.Enabled:=true;
  Timer1.Tag:=Timer1.Tag+40;
  Timer1.Interval:=100; // to można ustalić we wlaciwociach
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if Timer1.Tag>0 then
  begin
    Timer1.Tag:=Timer1.Tag-1;
    Label2.Tag:=Label2.Tag+1;
    Label2.Caption:=IntToStr(Label2.Tag);
  end
  else
  begin
    Timer1.Enabled:=false;
    Button2.Enabled:=true;
  end;
end;

end.
0

Witam Ponownie

Nawiązując do pomysłów bez użycia wątków znalazłem sposób jaki daje jakieś rezultaty ale nie rozwiązuje wszystkich problemów:

SendMessage(Handle, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(ColorButton3.Handle),BN_CLICKED), ColorButton3.Handle);

Ta linia powoduje wywołanie przycisku ColorButton3 tak jak by był naciśnięty przez myszkę, ale z jakiegoś powodu przycisk tak naciśnięty zachowuje się inaczej niż rzeczywiście nacieknięty przez mysz w aplikacji przez operatora.

Jak już pisałem celem tej zabawy jest zdalne naciśniecie przycisku po tym jak z portu szeregowego przyjdzie komenda naciśnij przycisk, przycisk jest naciskany ale kolejna komenda ulokowana już w ColorButton3 zadaje pytanie przez ten sam port szeregowy, i czeka na odpowiedz, i o ile zapytanie wychodzi przez port to odpowiedz zostaje w buforze aż do chwili dopóki alkilacja nie wywoła jakiegoś systemowego MessageBox i ten nie zostanie kliknięty.

Tak wiec proste pytanie czy możliwe jest całkowite za-symulowanie kliknięcia ColorButton3 tak jak by przycisnął to operator ?? lub jaka jest przyczyna ze ComDataPacket1Packet nie odbiera danych po tym jak wywoła SendMessage uruchamiającego ColorButton3 aż do naciśnięcia OK luk czegokolwiek na najbliższym pojawieniu się MessageBox ?? mimo ze port wykonuje transmisje wychodzącą ?

Pozdrawiam
Szymon

0

ColorButton3.Click;

0

No niestety zupełnie nie o to chodzi, dodam ze PostMessage też próbowałem z tm samym skutkiem.

w przypadku ColorButton1.Click jest jeszcze gorzej, bo ComDataPacket1Packet czeka na wykonanie ColorButton1 (ponad 2 minuty) zanim wykona resztę własnego kodu, a tu chodzi o to by ComDataPacket1Packet odebrała pakiet zanalizowała i wykonała naciśniecie ColorButton1.Click i zakończyła się. Procedura ColorButton1 która to ponownie zadaje pytanie przez port i oczekuje kolejnej odpowiedzi, analizy wykonanej ComDataPacket1Packet i która tego nie robi bo z jakiegoś powodu wisi, albo czeka na coś, nie mam pojęcia na co.

Pozdrawiam
Szymon

2

Witam !

Jednak zamiana Send na Post zmieniła sytuacje, i port szeregowy nie czeka już na nic tylko się wykonuje ;-)

PostMessage (Handle, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(ColorButton3.Handle),BN_CLICKED), ColorButton3.Handle);

Wim ze metoda pokręcona, podobnie jak strzelać z armaty do muchy (nie mylić z Anną M ;-) ) ale działa symuluje ładnie naciśniecie myszki i to to chodziło.

Pozdrawiam i dziękuje za pomoc.
Szymon

dodanie znaczników <code class="delphi"> - fp

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