Obliczenia w wątkach

0

Witajcie,

pisze program który ma przeprowadzić serię dość skomplikowanych obliczeń. Algorytm polega na tym że testuje kolejne zestawy argumentów pod kątem tego czy po serii obliczeń pasują do danego wzorca. Coś jakby brutal force tylko nie łamie haseł ;)

Program zorganizowałem tak że mam ileś wątków i po kolei przydzielam im parametry wejściowe, gdy w wątku zostanie obliczony wynik wystawia on flagę. Program główny w pętli cały czas sprawdza flagi i jak któraś jest wystawiona zczytuje wynik i zadaje nowe parametry.

Mój problem polega na tym że jak śledzę wątek w debugerze (krok po kroku) to wszystko liczy idealnie, natomiast gdy puszcze program "na żywo" wyniki są błędne. Zadaje ciągle te same parametry, a wyniki dostaje różne choć wzory są na 100% dobre;

Czy takie zwielokrotnione wątki mogą się zakłócać? Nie mam pojęcia co mogę zrobić, wydaje mi się to absurdalne, ale pierwszy raz korzystam z wątków do takich obliczeń i może robi się to jakoś inaczej?

pozdrawiam

0

Bez kodu niewiele można stwierdzić...

0

przede wszystkim zamiast sprawdzać w pętli w wątku głównym to wątek poboczny powinien informować (event, meassage) wątek główny, że skończył. Co do reszty to kod jest potrzebny

0

dzięki za szybką reakcje, po południu pokaże kod bo mam go na innym kompie.

pozdrawiam

0

Przygotowałem kod, trochę go uprościłem żeby łatwiej go analizować:

TTest = class(TThread)
 protected
   start,stop:integer;
   numery : TStringlist;
   TESTbuffer1: Array [0..9] of Byte;
   buffer1: Array [0..9] of Byte;
   DSYG: Array [0..7] of Byte;
   constructor Create(Buffer1z: Array of Byte);
   procedure Execute; override;
   procedure obliczenia1(parametr: Integer);
   procedure obliczenia2;
 public
   jest:boolean;
   trafiony:boolean;
   numer:integer;
   koniec:boolean;
 end;

constructor TTest.Create(Buffer1z: Array of Byte);
var
  j:byte;
begin
  inherited Create(true);

  FreeOnTerminate := True;
//wczytuje stałą do puźniejszych obliczeń
  for j := 0 to 9 do
  begin
    TESTbuffer1[j] := Buffer1z[j];
  end;
  jest:=true;
  koniec:=true;
  resume;
end;

procedure TTest.Execute;
var
  I : Integer;
  crt:byte;
begin
  while koniec do

  if not jest then
  begin
    trafiony:=false;

    obliczenia1(numer);
    obliczenia2;
    if pare_warunków then
    	trafiony:=true;
    jest:=true;
  end;
end;

procedure TTest.obliczenia1(parametr: integer);
var temp: Array [0..3] of Byte;
begin
//obliczenia na buffer, parametr i DSYG
end;

procedure TTest.obliczenia2; 
var i, j, x, y, z, temp, Pomocnicza: Byte;
    p: Shortint;
begin
//mnustwo obliczeń na buffer1 i TESTbuffer1

end;



procedure TForm1.Button4Click(Sender: TObject);
var

  i:integer;
Begin

w1 := TTest.Create(buffIN);
w2 := TTest.Create(buffIN);
w3 := TTest.Create(buffIN);
i:=0;
w1.Priority:=tpIdle;
w2.Priority:=tpIdle;
w3.Priority:=tpIdle;


while i<100001 do
begin
  Application.ProcessMessages;
  if i=100000 then
    sleep(1000);
  if w1.jest then
  begin
    if w1.trafiony then
      memo1.Lines.Add(inttostr(w1.numer));
    w1.numer:=i;
    inc(i);
    w1.jest:=false;
  end;

  if w2.jest then
  begin
    if w2.trafiony then
      memo1.Lines.Add(inttostr(w2.numer));
    w2.numer:=i;
    inc(i);
    w2.jest:=false;
  end;

  if w3.jest then
  begin
    if w3.trafiony then
      memo1.Lines.Add(inttostr(w3.numer));
    w3.numer:=i;
    inc(i);
    w3.jest:=false;
  end;
end;
w1.koniec:=false;
w2.koniec:=false;
w3.koniec:=false;

end;


 
0

Poczytaj może o synchronizacji wątków i o sekcjach krytycznych bo tu jest mnóstwo błędów.

0

No poczytałem o tym i nie widzę w tym kodzie sytuacji które mogły by powodować kolizje. Wątki między sobą nie mają nic wspólnego nie modyfikują nic na zewnątrz. Jeśli możesz napisz coś więcej proszę.

0
  1. W create tworzysz wstrzymany wątek, a potem dajesz resume. Bez sensu.
  2. W execute po zakończeniu obliczeń wątek się zawiesza w pętli "while koniec do". Bez sensu.
  3. Po co nadajesz priorytet tpIdle?
  4. Zamiast "if pare_warunków then trafiony:=true" użyj SendMessage, żeby powiadomić wątek główny o zakończeniu.
  5. Zawieszasz aplikację sprawdzając stan wszystkich wątków w pętli (button4click). Czy w takim razie w ogóle potrzebne Ci są wątki?
0

Może coś w ten deseń, trochę chaotycznie to poprawiłem, ale wg mnie widać w czym sęk:

type
  TTest=class;
  TTestEvent=procedure(Sender:TTest;var Numer:Integer;var Done:Boolean;Trafiony:Boolean)of Object;
  TTenBytes=array[0..9]of Byte;

  TTest=class(TThread)
  protected
    start,stop:Integer;
    numery:TStringlist;
    buffer1,TESTbuffer1:TTenBytes;
    OnEvent:TTestEvent;
    procedure GetJob;
    function HaveJob:Boolean;
    procedure Execute;override;
    procedure obliczenia1(parametr:Integer);
    procedure obliczenia2;
  public
    Trafiony:Boolean;
    Numer:Integer;
    constructor Create(const Buffer1z:TTenBytes;DoEvent:TTestEvent);
    destructor Destroy;override;
  end;

  TForm1 = class(TForm)
    Button4: TButton;
    Memo1: TMemo;
    procedure Button4Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    Pool:array[0..2]of TTest;
    buffIN:TTenBytes;
    iStep:Integer;
  public
    procedure OnEvent(Sender:TTest;var Numer:Integer;var Done:Boolean;Trafiony:Boolean);
  end;

var Form1: TForm1;

implementation

{$R *.DFM}

constructor TTest.Create(const Buffer1z:TTenBytes;DoEvent:TTestEvent);
begin
  inherited Create(true);
  FreeOnTerminate:=True;
  numery:=TStringlist.Create;
  TESTbuffer1:=Buffer1z;
  Priority:=tpLowest; // tpIdle - odradzam mieć taki w iloci więcej niż 1
  Trafiony:=false;
  Numer:=0;
  OnEvent:=DoEvent;
  Resume;
end;

destructor TTest.Destroy;
begin
  numery.Free;
  inherited Destroy;
end;

procedure TTest.GetJob;
var Done:Boolean;
begin
  Done:=true;
  OnEvent(Self,Numer,Done,Trafiony);
  if Done then Terminate;
end;

function TTest.HaveJob:Boolean;
begin
  Result:=not Terminated;
  if Result then
  begin
    Synchronize(GetJob);
    Result:=not Terminated;
  end;
end;

procedure TTest.Execute;
begin
  while HaveJob do
  begin
    Trafiony:=false;
    obliczenia1(numer);
    obliczenia2;
    Trafiony:=((Numer mod 3)=0)and((Numer mod 5)=0)and((Numer mod 7)=0);
  end;
end;

procedure TTest.obliczenia1(parametr:integer);
begin
//obliczenia na buffer, parametr i DSYG
end;

procedure TTest.obliczenia2;
begin
//mnustwo obliczeń na buffer1 i TESTbuffer1

end;

procedure TForm1.OnEvent(Sender:TTest;var Numer:Integer;var Done:Boolean;Trafiony:Boolean);
begin
  if Trafiony then Memo1.Lines.Add(IntToStr(Numer));
  Inc(iStep);
  Done:=(iStep>100000);
  if Done then Button4.Enabled:=true
  else Numer:=iStep;
end;

procedure TForm1.Button4Click(Sender: TObject);
var I:Integer;
begin
  iStep:=0;
  Button4.Enabled:=false;
  for I:=Low(Pool) to High(Pool) do Pool[I]:=TTest.Create(buffIN,OnEvent);
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var I:Integer;
begin
  for I:=Low(Pool) to High(Pool) do Pool[I].Terminate;
end;
ergo napisał(a):
  1. W create tworzysz wstrzymany wątek, a potem dajesz resume. Bez sensu.
    A jednak:
unit Unit22;

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

type
  TTest=class(TThread)
  protected
    procedure Execute;override;
  public
    Marker:Integer;
    constructor Create;
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    tst:TTest;
  end;

var Form1: TForm1;

implementation

{$R *.DFM}

constructor TTest.Create;
begin
  Marker:=0;
  Form1.Memo1.Lines.Add('Before create');
  inherited Create(false);
  Form1.Memo1.Lines.Add('After create');
  while Marker<>1 do Application.ProcessMessages;
  Form1.Memo1.Lines.Add('Wciaz ustawiam parametry watku');
  Marker:=2;
end;

procedure TTest.Execute;
begin
  Form1.Memo1.Lines.Add('Thread done');
  Marker:=1;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.Memo1.Lines.Add('Start program');
  tst:=TTest.Create;
  while tst.Marker<>2 do Application.ProcessMessages;
  Form1.Memo1.Lines.Add('Exit program');
  tst.Free;
end;

end.
0

Poczytaj może o synchronizacji wątków i o sekcjach krytycznych bo tu jest mnóstwo błędów.

True:

TTest = class(TThread)
  [...]
  public
   jest:boolean;
   trafiony:boolean;
   numer:integer;
   koniec:boolean;
 end;

Wątki między sobą nie mają nic wspólnego nie modyfikują nic na zewnątrz.

Nie no, serio? A może jednak?

//wczytuje stałą do puźniejszych obliczeń

Ja tu widzę błąd, tylko że z TBrain.

Jak nic mądrego nie umiesz powiedzieć to się nie produkuj bez sensu, A najlepiej skasuj swój post i napisz jeszcze raz.

Akurat to ty nic mądrego nie umiesz zrobić i dlatemu przychodzisz na forum. Najlepiej skasuj się i stwórz się raz jeszcze. Akurat on jedyny wskazał poprawną przyczynę twoich problemów.

  1. Zawieszasz aplikację sprawdzając stan wszystkich wątków w pętli (button4click). Czy w takim razie w ogóle potrzebne Ci są wątki?

Wiesz, to ta magia większej wydajności która jest mitem :>
Na dodatek koleś nie dość że bez sensu zakłada że potrzebuje wątków to jeszcze nie umie ich użyć...

To pierwsze jest jak najbardziej z sensem. Widać jeszcze nie zetknąłeś się z przypadkiem kiedy wątek się skończył zanim dałeś FreeOnTerminate:=true;

Wytłumacz mi, jak konstruktor, który jest wykonywany z kontekstu głównego wątku może nie zdążyć przed przyznaniem czasu wątkowi.

Create wątku już się wykonała w inherited Create... na starszych Delphi ciągle był ten problem, pamiętam jak straciłem kilka tygodni na szukanie co się chrzani :/ Wydawało mi się że od 7-ki już działa dobrze, a jednak nie. Dopisze przykład do postu na dole.

No to co by się stało gdybyś kontrolę do inherited oddał dopiero po ustawieniu pól... Bo moim zdaniem to czego osiągnąć ty nie umiesz bez zawieszania...

A jednak:

A jednak co? Nawet dziecko wie że inherited wywoła metody odpowiadające za tworzenie wątku.
Jeżeli nie jesteś na tyle genialny żeby zrobić to co trzeba przed inherited to równie dobrze możesz to zrobić z poziomu nowego wątku, czyż nie? I gdzie tu problem? Bo moim zdaniem to problem jest sztuczny.

0

"Wiesz, to ta magia większej wydajności która jest mitem" mój raytracer działa kilkukrotnie razy szybciej, jeżeli działa z wątkami (każdy renderuje n-tą linię), więc nie jest to taki do końca mit ;]

To zależy czy się umie podzielić odpowiednio kod.

"Wytłumacz mi, jak konstruktor, który jest wykonywany z kontekstu głównego wątku może nie zdążyć przed przyznaniem czasu wątkowi." - przykład dokleiłem, wystarczy skompilować.

No a ja wytłumaczyłem, dlaczemu to jest idiotyzm. Wyciągasz z kontekstu.

"No to co by się stało gdybyś kontrolę do inherited oddał dopiero po ustawieniu pól" - A co z FreeOnTerminate, które może w kolejnej wersji zostać nadpisane wewnątrz inherited?

Ależ oczywiście, na pewno nie jest to zaprojektowane tak aby to tak robić. Poza tym, dalej masz inne rozwiązanie, z ustawianiem tego w nowym wątku. Ale ty sobie postanowiłeś, że będziesz to usilnie robić źle. I w tym celu zaprzęgłeś opcję zawieszania, żeby pokazać że źle też to można zrobić...

Więcej oleju w głowie, to nie będziesz musiał stawać na głowie żeby wyważyć otwarte drzwi.

0
gdgd napisał(a):

Akurat to ty nic mądrego nie umiesz zrobić i dlatemu przychodzisz na forum. Najlepiej skasuj się i stwórz się raz jeszcze. Akurat on jedyny wskazał poprawną przyczynę twoich problemów.

Przepraszam "boże".

A pozostałym serdecznie dziękuje.

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