Kilka wątków zmieniających Caption Labeli.

0

Witam,
Od kilku dni próbuję napisać aplikację w delphi 7, która zmieniłaby captiony 3 labeli ale w 3 osobnych wątkach (label1.caption zmienia się w pierwszym wątku, label2.caption zmienia się w drugim i label3.caption zmienia się w 3 wątku). Ale chciałbym by wszystkie captiony zmieniły się w dokładnie tym samym momencie.
Na 4programmers znalazłem taki, kod, losuje on jakąś maksymalna wartość progressbaru i przypisuje do każdego wartość licznika "i" w pętli aż do wylosowanej maksymalnej wartosci. Pierwszy problem jaki zauważyłem, to to że nie dzieje się to synchronicznie, zawsze jest jakaś przerwa między "przeskakiwaniem tych kresek". A drugi problem jest taki, że gdy na formę wrzucę jakiś label i chcę zmienić jego caption w procedurze Execute to wywala mi:

[Pascal Error] Unit1.pas(1): Unable to invoke Code Completion due to errors in source code
Ciekawa też rzecz jest taka, że jak z menu wybiorę stworzenie nowego wątku to tworzy mi jakby drugi plik z kodem "Unit2", a cała forma jest na Unit1 i co bym nie wrzucił (button, edit, label, memo) to stworzony wątek już go nie rozpozna :P

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    btnGo: TButton;
    ProgressBar1: TProgressBar;
    ProgressBar2: TProgressBar;
    ProgressBar3: TProgressBar;
    procedure btnGoClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


 TGoThread = class(TThread)
  private
    FV : Integer; // wylosowana liczba
    FCounter : Integer; // numer wątku
    pozycja : Integer;
    procedure SetProprties;
  protected
    procedure Execute; override;
  public
    constructor Create(Counter : Integer);
  end;


var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnGoClick(Sender: TObject);
begin
{ utworzenie trzech wątków }
  TGoThread.Create(1);
  TGoThread.Create(2);
  TGoThread.Create(3);
end;

constructor TGoThread.Create(Counter: Integer);
begin
  inherited Create(False); // wywołanie wątku
  FCounter := Counter; // przypisanie wartości do zmiennej
end;

procedure TGoThread.Execute;
var
  i : Integer;
begin
  FreeOnTerminate := True; // zwolnij po zakończeniu wątku

  Randomize;
  FV := Random(1000);
  { odnalezienie komponentu na formularzu }
  TProgressBar(Form1.FindComponent('ProgressBar' + InttoStr(FCounter))).Max := FV;
 
  for i := 0 to FV do
  begin
    Sleep(10);
    pozycja := i;
    Synchronize(SetProprties);
  end;
end;
 
procedure TGoThread.SetProprties;
begin
    TProgressBar(Form1.FindComponent('ProgressBar' + IntToStr(FCounter))).Position := pozycja;
end;
 
end.
0

Więc niech wątki zmieniają jakieś trzy niezależne zmienne (np właściwość Hint). Uwaga, synchronizacja nadal obowiązuje.
Natomiast jakiś timer przypisuje z Hint do Caption.

0

pod D2010 podany kod działa bez problemu
co do wątku w oddzielnym unice , to pewnie brakuje w nim

uses unit1; 

i jeszcze jedno , każdy wątek po utworzeniu żyje swoim życiem , nie ma żadnej gwarancji że aktualizacje captionów i progressbarów będą się wykonywały synchronicznie
jest raczej pewne że kolejność aktualizacji będzie zupełnie przypadkowa
jeśli chcesz wszystkie obiekty aktualizować synchronicznie , to najprościej zrobić to w jednym wątku
oczywiście można by zbudować mechanizm synchronizacji trzech wątków tak aby aktualizacje odbywały się synchronicznie

1

Ogólnie - tak się nie da. Nie da się ponieważ zmiany w UI mogą być robione TYLKO z poziomu wątku głównego. Koniec, kropka. Ten fakt determinuje stwierdzenie, że jednocześnie się nie da.

0

masz rację, niezbyt precyzyjnie się wyraziłem.
mówiąc o zbudowaniu własnego mechanizmu synchronizacji myślałem o stworzeniu czegoś takiego aby aktualizacje obiektów w trzech oddzielnych wątkach wykonywały się zawsze w ustalonej kolejności: label1,label2,label3,label1,label2 ......
a równoczesna ich aktualizacja , tak jak piszesz , nie jest możliwa
zresztą sama metoda Synchronize wykonuje się w kontekście wątku głównego

0
grzegorz_so napisał(a):

zresztą sama metoda Synchronize wykonuje się w kontekście wątku głównego

tak, tylko że jak wywołasz z trzech wątków synchronize nawet jednocześnie (bo może się tak zdarzyć) to i tak wątek główny jest jeden i on te wywołania "wewnętrznie" będzie kolejkował i tego się nie przeskoczy

0

ależ dokładnie tak będzie , zupełna zgoda
myślałem o stworzeniu mechanizmu kolejkującego "synchronize" z poszczególnych wątków, tak aby były wywoływane w zadanej kolejności
oczywiście mechanizm należało by zaimplementowac w samym wątku, tak aby wątek czekał na swoją kolej z wywołaniem swojego "synchronize"

0

tylko czy to ma sens bo może się zdarzyć (i pewnie nie będzie to wyjątek), że wątek 1 "obróci się" dwa razy a wątek 2 i 3 tylko raz. Potem 1 wcale a 2 np. trzy razy. I już całe kolejkowanie (zbieranie zawsze wyników z trzech wątków) nie ma sensu. Dlatego ktoś wymyślił wątki aby kawałki kodu mogły się wykonywać niezależnie od siebie

0

skutek będzie taki że najwolniejszy w danym cyklu wątek będzie wstrzymywał pozostałe .
a czy ma sens ??? pytający postawił taki problem więc stąd dyskusja .
Chociaż pewnie dało by się znaleźć sytuacje w których taki mechanizm synchronizacji cykli wątków mógł by mieć sens.

0

Trochę ściema była z tymi captionami, ogólnie miał to być program oparty na komponencie THGG (który o dziwo jeszcze działa), wysyłający z x numerów x wiadomości w tym samym czasie (tak tak, wiem że pomyślicie, że to spam, ale chodziło mi raczej o reklamę). W C napisałem aplikację konsolową pod Debianem, opartą na bibliotece libgadu, ale chciałem żeby to jakoś wyglądało i wziąłem się za delphi 7 :P

Tu jest wszystko na jedno kopyto w głównym wątku, program po kliknięciu logowania albo wysłania ścina na czas wykonywania np. pętli. Chodziło mi o wysyłanie tych wiadomości z innego/ych wątku/ów.

unit ggy;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, HGG,
  ExtCtrls, StdCtrls;

type
  Tsend = class(TForm)
    vnumber_label: TLabel;
    vnumber: TEdit;
    vmsg_label: TLabel;
    vmsg: TEdit;
    login: TButton;
    logout: TButton;
    timer: TTimer;
    HGG1: THGG;
    exit: TButton;
    start: TButton;
    HGG2: THGG;
    HGG3: THGG;
    HGG4: THGG;
    HGG5: THGG;
    HGG6: THGG;
    HGG7: THGG;
    HGG8: THGG;
    HGG9: THGG;
    HGG10: THGG;
    HGG11: THGG;
    HGG12: THGG;
    HGG13: THGG;
    HGG14: THGG;
    HGG15: THGG;
    HGG16: THGG;
    HGG17: THGG;
    HGG18: THGG;
    HGG19: THGG;
    HGG20: THGG;
    HGG21: THGG;
    HGG22: THGG;
    HGG23: THGG;
    HGG24: THGG;
    HGG25: THGG;
    HGG26: THGG;
    HGG27: THGG;
    HGG28: THGG;
    Button1: TButton;
    pass: TEdit;
    Label1: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label2: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Label18: TLabel;
    Label19: TLabel;
    Label20: TLabel;
    Label21: TLabel;
    HGG29: THGG;
    HGG30: THGG;
    HGG31: THGG;
    HGG32: THGG;
    HGG33: THGG;
    HGG34: THGG;
    HGG35: THGG;
    HGG36: THGG;
    HGG37: THGG;
    HGG38: THGG;
    HGG39: THGG;
    HGG40: THGG;
    HGG41: THGG;
    HGG42: THGG;
    HGG43: THGG;
    HGG44: THGG;
    HGG45: THGG;
    HGG46: THGG;
    HGG47: THGG;
    HGG48: THGG;
    HGG49: THGG;
    HGG50: THGG;
    HGG51: THGG;
    HGG52: THGG;
    HGG53: THGG;
    HGG54: THGG;
    HGG55: THGG;
    HGG56: THGG;
    HGG57: THGG;
    HGG58: THGG;
    HGG59: THGG;
    HGG60: THGG;
    HGG61: THGG;
    HGG62: THGG;
    HGG63: THGG;
    HGG64: THGG;
    HGG65: THGG;
    HGG66: THGG;
    HGG67: THGG;
    HGG68: THGG;
    HGG69: THGG;
    HGG70: THGG;
    HGG71: THGG;
    HGG72: THGG;
    HGG73: THGG;
    HGG74: THGG;
    HGG75: THGG;
    HGG76: THGG;
    HGG77: THGG;
    HGG78: THGG;
    HGG79: THGG;
    HGG80: THGG;
    HGG81: THGG;
    HGG82: THGG;
    HGG83: THGG;
    HGG84: THGG;
    HGG85: THGG;
    HGG86: THGG;
    HGG87: THGG;
    HGG88: THGG;
    HGG89: THGG;
    HGG90: THGG;
    HGG91: THGG;
    HGG92: THGG;
    HGG93: THGG;
    HGG94: THGG;
    HGG95: THGG;
    HGG96: THGG;
    HGG97: THGG;
    HGG98: THGG;
    HGG99: THGG;
    HGG100: THGG;
    procedure exitClick(Sender: TObject);
    procedure loginClick(Sender: TObject);
    procedure logoutClick(Sender: TObject);
    procedure startClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

const
  txtPlik = 'dane.txt';

var
  send: Tsend;
  Tablica: array[1..100] of Integer;
  txtDane :String;
  TF      :Text;
  a       :Integer;

implementation

{$R *.dfm}

procedure Tsend.exitClick(Sender: TObject);
begin
close;
end;

procedure Tsend.loginClick(Sender: TObject);
var
   I: Integer;
begin
For I:=1 To a-1 do
begin
THGG(FindComponent('hgg'+IntToStr(I))).Number:=Tablica[I];
THGG(FindComponent('hgg'+IntToStr(I))).Password:=pass.Text;
THGG(FindComponent('hgg'+IntToStr(I))).Status:=1;
THGG(FindComponent('hgg'+IntToStr(I))).Connect(500);
end;



end;
procedure Tsend.logoutClick(Sender: TObject);
var
   I: Integer;
begin
For I:=1 To a-1 do
begin
THGG(FindComponent('hggg'+lntToStr(I))).Status:=0;
end;

end;
procedure Tsend.startClick(Sender: TObject);
var
   I: Integer;
begin

For I:=1 To a-1 do
THGG(FindComponent('hgg'+lntToStr(I))).123,vmsg.text,);

end;

procedure Tsend.Button1Cl1ck(Sender: TObject);
begin
a:=1;
AssignFile(TF, txtPlik);
If Not FileExists(txtPlik) Then
begin
  ReWrite(TF);
  ShowMessage('Utworzono plik dane.txt, uzupelnij go lista numerow i ponownie wczytaj.');
  end;
Reset(TF);
while not EOF(TF) do
begin
txtDane:='';
ReadLn(TF, txtDane);
Tablica[a]:=StrToInt(txtDane);
a:=a+1;
end;
CloseFile(TF);


end;

procedure Tsend.FormCreate(Sender: TObject);
begin

end;

end.

Wprowadziłem kilka błędów celowo :P

0

Całkiem źle się za to zabrałeś...
Komponent THGG "wkładasz" w wątek. Wątek ma generalnie dwa zadania - 1 - zalogować się do GG i 2 - wysłać wiadomość na przekazaną listę numerów GG. Teraz każdy wątek ma swoich "klientów" do obsłużenia i nie blokuje wątku głównego. A dlaczego nie wysyłać jeden wątek jedna wiadomość? bo po pierwsze wątki "kosztują" zasoby a i liczba gniazd dostępnych w systemie jest ograniczona. O ile odpalenie 50 czy 100 to nie zbrodnia to już np. 10000 to morderstwo. A po drugie przy dużej ilości wątków może być wolniej zamiast szybciej.

0

Ale gdyby każdy wątek logował 10 numerów i wysyłał z każdego po jednej wiadomości nie byłoby tak źle, na 100 numerów przypadałoby 11 wątków wraz z głównym. Problem jednak pojawia się z realizacją.

Czy jest jakiś uniwersalny przykład kodu, który tworzy np. 3 wątki i dla każdego z nich można było napisać oddzielne funkcje, tak by wszystkie wykonane były synchronicznie?

Nigdy wcześniej nie miałem do czynienia z wątkami (może ukradkiem je gdzieś liznąłem, ale to był C), dużo góóglowałem, ale przykładów z delphi mało jest :)

2
unit Unit1;

interface

uses
  Classes {$IFDEF MSWINDOWS}, Windows {$ENDIF};

type
  ggmessage = record
    number, body: string;
  end;

type
  Tggthread = class(TThread)
  protected
    ggclient: thgg;
    ffilename: string;
    ggmessages: array of ggmessage;
    procedure Execute; override;
  public
    constructor Create(filename: string);
  end;

implementation

constructor Tggthread.Create(filename: string);
begin
  self.ffilename := filename;
end;

procedure Tggthread.Execute;
var
  I: Integer;
begin
  FreeOnTerminate := True;
  try
    self.ggclient := thgg.Create(param1, param2, .. , .. );

    // tutaj odczyt z pliku ffilename numerów i tekstów do wysłania
    // oraz  zapis w tablicy ggmessages

    self.ggclient.connect;
    for I := 0 to high(self.ggmessages) do
      self.ggclient.send(self.ggmessages[I].body, self.ggmessages[I].number);
    self.ggclient.close;

  finally
    self.ggclient.free;
  end;

end;

end.

oraz na formie głównej pod jakimś buttonem

Tggthread.Create('file1.txt');
Tggthread.Create('file2.txt');
Tggthread.Create('file3.txt');

nie znam komponentu THgg więc nazwy metod i ich parametry przyjąłem dość dowolnie, chodzi o sam schemat
oczywiście każdy z watków będzie się wykonywał niezależnie , więc o jakiejkolwiek synchroniczności nie może być mowy

0

Dzięki,
Jutro zabieram się do pracy ;)
temat do zamknięcia

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