Thread - dwa procesy wykonywane jednoczesnie

0

Witam

Załóżmy, że mamy taki prosty programik, w którym staram się uruchomić dwa procesy jednocześnie. Przykładowo pętlę zmieniającą etykietę przycisku oraz drugi wątek, który zmienia kolor panela.

var
  Form1: TForm1;
  Watek: TWatek;
//..........
procedure TForm1.Button1Click(Sender: TObject);
  var n: Integer;
begin
  Watek.Create(False);
  //Jakaś procedura trwająca nieprzerwanie kilka sekund, np pętla for :
  for n := 200 downto 0 do
  begin
    Button1.Caption := IntToStr(n);
    Sleep(20);
  end;
end;

procedure TWatek.Execute;
begin
  inherited;
  FreeOnTerminate := True;
  Randomize;
  while not Watek.Terminated do
  begin
    Form1.Panel1.Color := RGB(Random(255), Random(255), Random(255));
    sleep(250);
  end;
end;

Wątek wywoływany jest przed procedurą, jednak wykonywany zostaje dopiero po jej skończeniu. Dlaczego nie wykonują się jednocześnie? Nadawanie priorytetu dla wątku nic nie daje.

0
  1. To nie jest jednoczesne uruchomienie dwóch procesów.

  2. Używaj Synchronize do odwoływania się do komponentów z Wątku.

  3. Jak już się silisz na wklejenie kodu źródłowego, to go przeklej a nie pisz od nowa w oknie posta (Watek := TWatek.Create(False), a nie Watek.Create(False))

  4. Nie jestem pewien, ale chyba sęk w tym że forma główna nie przetwarza komunikatów bo jest zajęta. Więc albo w pętli daj Application.ProcessMessages, albo zrób dwa wątki, jeden zmiania kolor drugi ustawia captiona.

0

dawno nie bawilem sie watkami ale nie powinno byc przypadkiem tak:

Watek := TWatek.Create(False);

??

//EDIT:
Sorka, nie zauważyłem postu wyżej (pojawił sie jak wysłałem)

//EDIT 2:
Spróbuj też dać Application.ProcessMessages zaraz po utworzeniu wątku, albo utwórz go uśpionego zaraz po tym (przed processmessages) daj Watek.Resume;

0

Przykład pisałem w locie no i rzeczywiście powinno być Watek := TWatek.Create(False);
Ale i tak nic to nie zmienia. Co do Application.ProcessMessages w tym wypadku oczywiscie pomoże, ale ja tylko podałem przykład pętli jako procedury która trwa określony czas. W moim programie nie ma procedury powtarzanej cyklicznie. Jest jedna, która łączy się z bazą danych i wykonuje pewne operacje. Trwa to do kilku sekund. Jednocześnie z tą operacją muszę wykonać drugą równoległą, ale pÓÓÓÓki co operacje mi się "kolejkują". Synchronizacja też nic nie daje.
Nie mogę również utworzyć drugiego wątku bo ten fragment kodu znajduje sie w DLL-ce i nie mogę wpływać na działanie głównego programu.

0

A nie możesz tych operacji na bazie wrzucić do wątku ?

0

Właśnie sęk tkwi w tym że nie mogę w nie ingerować. Założenie jest takie, że wątek ma śledzić działanie innych procesów. Jest w pewnym momencie wywoływany i musi działać jednocześnie z nimi. Teoretycznie powinno to działać, w praktyce jest inaczej.

Przykładem może być również okno z animacją oczekiwania, którą wyświetlamy podczas gdy program główny jest zajęty wykonywaniem innych operacji.

0

W przykładzie poniżej procedura w wątku czasem wykona się raz na samym początku, a później po upływie 4s.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Watek := TWatek.Create(True);
  Watek.Priority := tpHigher;
end;

procedure TForm1.Button1Click(Sender: TObject);
  var n: Integer;
begin
  Watek.Resume;
  Application.ProcessMessages;
  Sleep(4000);
end;

procedure TWatek.Execute;
begin
  inherited;
  FreeOnTerminate := True;
  Randomize;
  while not Watek.Terminated do
  begin
    Synchronize(Form1.Procedura);
    sleep(250);
  end;
end;

procedure TForm1.Procedura;
begin
  Form1.Panel1.Color := RGB(Random(255), Random(255), Random(255));
end;

a jeśi wprowadzimy taką modyfikację:

  Watek.Resume;
  Sleep(4000);
  Watek.Terminate;

to wątek nie wykona się wcale

0

No a jakbyś spróbował zamiast tej zmiany koloru panel'a robić coś innego ? np wysyłaj komunikaty do jakiegoś innego programu, który będzie na nie jakoś reagował (chodzi mi o to żebyś mógł zaobserwować to działanie). Wydaje mi się że to jest tak, że jak główny wątek jest zajęty, to nic nie odmaluje. Tylko tak się zastanawiam, czy jakby ten wątek nie miał nic do roboty związanego z główną formą, to czy się wtedy wykona czy nie ... ale już sam nie wiem, trzeba by to sprawdzić.

0

Jeśli napisze cos takiego:

var
  Form1: TForm1;
  Watek: TWatek;
  K : integer;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Watek := Twatek.Create(False);
  Sleep(4000);
  Watek.Terminate;
  self.Caption := inttostr(k);
end;

procedure TWatek.Execute;
begin
  inherited;
  FreeOnTerminate := True;
  Randomize;
  while not Watek.Terminated do
  begin
    inc(k);
   Form1.Panel1.Left := Form1.Panel1.Left + 1;
    Application.ProcessMessages;
    sleep(100);
  end;
end;

to K bedzie rowne 1, pętla wykonała się jeden raz.

Ale jeśli usunę wpis Form1.Panel1.Left := Form1.Panel1.Left + 1; to k będzie równe 40, czyli prawidłowa wartość. A to będzie oznaczało, że w tym wypadku wątek zadziałał poprawnie a najwyraźniej gryzie się Z VCL-em. Jest to niby jakiś postęp ale w swoim programie muszę się odwoływać do komponentów wizualnych.
Stworzyłem również dynamicznie komponent wewnątrz wątku - nic to nie dało.

0

Nie jestem w tym jakoś szczególnie dokształcony, ale wydaje mi się że problem polega na tym iż większość właściowości kontrolek VCL'owych ustawianych jest poprzez wysyłanie komunikatów i co za tym idzie ich przetwarzanie przez główny wątek. Jeśli ten z kolei 'wisi' bo coś tam grzebie w bazie, to tych komunikatów nie przetworzy i już. Ostatni eksperyment po części to potwierdził. Pytanie więc, czy np ten monitorujący wątek, nie mógł by wysyłać komunikatów do okna innej aplikacji - monitorującej ? a ona zajęła by się ich wizualizacją ?

0

Chciałbym uniknąć takiego rozwiązania.

0

To ja Ci już więcej nie pomogę, może ktoś jeszcze coś podpowie, ale mi się wyczerpały pomysły.

Z pewną taką nieśmiałością (wynikającą z niewiedzy) jestem gotów jednak stwierdzić, że w czasie gdy twoja aplikacja wykonuje np długie zapytanie na bazie w ramach wątku głównego, to zmusić ją do wizualizacji (kontrolkami) czegokolwiek jest niemożliwe.

Pozdrawiam

A co to za komponenty do bazy ? Niektóre mają asynchroniczne możliwości wykonywania zapytać (np ADO).

0

nie inicjujesz wartości k, więc tak naprawdę nie wiesz ile razy pętla się wykonała (przypadkiem zmienne globalne są inicjowane zerami, więc k = 1 faktycznie oznacza jedną iterację - przypadkiem).
Application.ProcessMessages() to chyba pomyłka, wiesz co ta metoda robi? przetwarza kolejkę zdarzeń. zdarzenie może otrzymać tylko wątek, który utworzył okno - więc przetwarzasz pustą kolejkę marnując czas procesora. poczytaj sobie tutaj: http://www.experts-exchange.com/Programming/Languages/Pascal/Q_23220045.html.
poza tym NIGDY nie odwołuj się, a już szczególnie NIE ZMIENIAJ wartości parametrów komponentów VCL i NIE WYWOŁUJ ich metod z więcej niż jednego wątku w niezsynchronizowany sposób (wszystko jedno, czy przez Synchronize, czy sekcje krtyczne/muteksy/cokolwiek innego). nie są one thread-safe, a to daje gwarancję sypania co jakiś czas losowymi błędami AV.

tak jak b0bik napisał: zablokowanie głównego wątku powoduje, że żadne komunikaty dotyczące zmian w kontrolkach nie będą przetworzone (jedyne rozwiązanie - stworzyć CAŁE OKNO w innym wątku niż wątek obsługujący bazę danych. wątek może obsługiwać tylko kontrolki z własnego okna, stąd stworzenie samej kontrolki i wrzucenie jej do okna stworzonego przez inny - stojący - wątek jest bez sensu), dodatkowo wywołanie ProcessMessages wiesza wątek, z którego to wywołanie następuje, stąd k=1. gdy główny wątek się "odwiesza" i zaczyna mielić komunikaty, wątek poboczny powinien odetkać się i przetwarzać pętlę dalej w normalny sposób.

poza tym - USE DEBUGGER, GEORGE! postaw breakpointa lub dwa, użyj trace to, wykonaj trochę kodu linijka po linijce. żyjemy w 21 wieku, nie musisz uruchamiać kodu w głowie, a następnie dziurkaczem przenosić go na taśmę papierową. mamy debugery i możemy zobaczyć jak zachowuje się kod linijka po linijce bez zgadywania.

0

Jeśli chodzi o inicjalizację K, zrobiłem to w OnCreateForm, ale nie wkleiłem do postu aby skrócić kod. O Synchronize wiem i stosuję. Teraz mam prośbę aby ktoś mi podpowiedział jak można stworzyć stworzyć przycisk dynamicznie w wątku i obsłużyć go poprawnie. W przypadku procedury poniżej przycisk się tworzy po zakończeniu procedury głównej a nie w trakcie jej trwania:

procedure TWatek.Execute;
begin
  inherited;
  FreeOnTerminate := True;
  Synchronize(Form1.UtworzPrzycisk);
end;

procedure TForm1.btn1Click(Sender: TObject);
  var n: Integer;
begin
  //UtworzPrzycisk;
  Watek := TWatek.Create(False);
  for n := 0 to 20 do Sleep(100);
end;

procedure TForm1.UtworzPrzycisk;
 var btn : TButton;
begin
  btn := TButton.Create(Form1);
  btn.Parent := Form1;
  btn.Width := 100;
  btn.Height := 70;
  btn.Left := 100;
  btn.Top := 100;
  Application.ProcessMessages;
end;

Jeśli wywołam procedurę UtworzPrzycisk w sekcji btn1Click (jak widać na przykladzie powyżej), to oczywiście utworzy się przed procedurą ale nie w wątku.

0

Nie chcę się mądrzyć ale zmienne globalne zawsze są inicjowane zerami w przeciwieństwie do lokalnych (Vademecum profesionalisty D4 strona 57). Inną cechą odróżniającą je od zmiennych lokalnych jest możliwość inicjalizacji w miejscu deklaracji - co gorąco polecam, bo wpływa na podniesienie czytelności kodu.

BTW masz wykupiony abonament na exp-ex ? Drogie to jest ? Opłaca się ?

0

przycisk tworzysz i dodajesz na formularz dokładnie w taki sam sposób niezależnie od wątku. ALE przycisk ten ZAWSZE będzie obsługiwany przez wątek, który stworzył dane okno. tak to działa pod windows, komunikaty do wszystkich elementów danego okna przychodzą hurtem do twórcy nie kontrolki, a okna. nic na to nie poradzisz.

@b0bik: napisałem o inicjowaniu, więc po co powtarzasz?

0

A w jaki sposób stworzyć formę dynamicznie w wątku?
Ten poniższy nie zdaje egzaminu:

var
  Form1: TForm1;
  Watek: TWatek;
  frm: TForm;
.....................................................
procedure TForm1.btn1Click(Sender: TObject);
  var n: Integer;
begin
  Watek := TWatek.Create(False);
  Sleep(5000);
end;

procedure TWatek.Execute;
begin
  inherited;
  FreeOnTerminate := True;
  Synchronize(Form1.UtworzForme);
end;

procedure TForm1.UtworzForme;
begin
  frm := TForm.Create(Form1);
  //frm.Parent := Form1;
  frm.Left := 100;
  frm.Top := 100;
  frm.Width := 400;
  frm.Height := 300;
  frm.Show;
end;
0

Samo Synchronize() to trochę mało. I mało wygodne. Żeby mieć sensowne, szybkie i stabilne działanie wątków, trzeba używać funkcji WinAPI z seri Interlocked...(), eventów, sekcji krytycznych itp. Trzeba trochę poczytać, ale to się naprawdę opłaci.

0

bosz, ile można tłumaczyć... [glowa] wrzodów można dostać i wyłysieć z nerwów. jak nie rozumiesz co sie do ciebie pisze, to najpierw poczytaj o temacie.
Synchronize() wykonuje kod W KONTEKŚCIE GŁÓWNEGO WĄTKU procesu. rozumiesz co to znaczy? to co robisz, to konstruowanie formatki w głównym wątku, mimo iż wywołanie tworzenia następuje z wątku - bo przez synchronize początek wywołania jest w wątku A, a koniec już w wątku B. czyli rodzicem formatki jest B.
jak już uda ci się poprawnie i we właściwym watku utworzyć okno, to będziesz potrzebował w nim zaimplementować obsługę pętli komunikatów. błagam, najpierw poczytaj w internecie jak się to robi, zanim wrócisz tu z kolejnym bezmyślnie skleconym kawałkiem kodu.

@Azarien - nie widzisz, że problem jest kompletnie w innym miejscu?

0
ŁF napisał(a)

@b0bik: napisałem o inicjowaniu, więc po co powtarzasz?

Nie przewidziałem że autor wstawi jeszcze jedną odpowiedź. Nie powtarzam się, ta odpowiedź była do Ciebie (mogłem napisać @ŁF:). Chodziło mi o to że określiłeś że zmienna jest zainicjowana przypadkiem, a według mnie to nie przypadek tylko zasada.

Drugie pytanie też było do Ciebie ŁF. Czy masz wykupiony "abonament" na experts-exchange i czy warto (i ile to kosztuje).

Pozdrawiam

0

hmmm, odpowiedziałem nie na to, co trzeba ;-)
nie, nie mam abonamentu. google i ciut spostrzegawczości wystarcza.

0

Eh no popatrz, nigdy bym nie sprawdził.
thx ;]

0
ŁF napisał(a)

hmmm, odpowiedziałem nie na to, co trzeba ;-)
nie, nie mam abonamentu. google i ciut spostrzegawczości wystarcza.

a możesz powiedzieć w jaki sposób to czytasz?

// od lewej do prawej - Ł

0

W taki jak google.

0

Szczerze powiedziawszy, to spodziewałem się że znajdę tam cuda na kiju : ) a tu takie sobie porady. Podejrzewam tylko że bardziej się Ci odpowiadacze przykładają, bo im za to płacą. I informacje tam zawarte powinny być w miarę pewne. W zasadzie zawsze jak czegoś szukałem, to pojawiały się jakieś linki dot. exp-exch, i myślałem sobie, tak ... jakbym zapłacił to już bym wiedział, a tak trzeba szukać. Lecz tak naprawdę jak się chce to się znajdzie i bez exp-exch.

0

jak byś zapłacił, to byś nie miał pięciu ekranów statycznych reklam. dalej byś musiał szukać ;]
chyba, że byś płacił naprawdę dużo, wtedy bym szukał za Ciebie.

0

Hehe.

Z drugiej strony bardzo mają fajną robotę Ci solucjanci. Ciekawe ile mają za każdą zaakceptowaną solucję. Ciekawe też jak się dzielą kasą. Pewnie abonament od leszczy idzie poczęści dla solucjonarzy i szefa całego burdla... jedna sztaba sinior Siarra jedna sztaba sinior Lipski ;]

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