Link to home
Start Free TrialLog in
Avatar of emu10k1
emu10k1

asked on

Problem with TThread

Hi, my problem is when I create an instance of TCombinationThread and start the method execute, it do the job but can't access any component of main form... when I try to access, for example, a label or a button, it generate an exception.

the class descending TThread:

type
  TCombinationThread = class(TThread)
  private
    FValue: String;
    FMin, FLevel: Integer;
    FList: TStrings;
    procedure FindCombinations(Value: string; Min, Level: Integer);
  protected
    procedure Execute; override;
  published
    property Value: String read FValue write FValue;
    property Min: Integer read FMin write FMin;
    property Level: Integer read FLevel write FLevel;
    property List: TStrings read FList write FList;
end;

procedure TCombinationThread.FindCombinations(Value: string; Min, Level: Integer);
var
  I: Integer;
begin
  if (Level > 0) then begin
    for I := Min to 25 do
      if frmGerarCombinacao.verifica(I) then
        FindCombinations(Format('%s- %2.2d ', [Value, I]), I + 1, Level - 1);
  end
  else begin
    FList.Add(CuringasText + Copy(Value, 2, Length(Value)));
  end;
end;

procedure TCombinationThread.Execute;
begin
  FindCombinations(FValue, FMin, FLevel);
  Form1.Label1.Caption:='testing'; // for example
  ExitThread(0);
end;

I think that when I try to write or read any component on Main form, it generate the error message.

Please, help me.
Avatar of emu10k1
emu10k1

ASKER

the error is on Form1.Label1.Caption:='testing'; // for example

please, how I can solve this problem?
You have to syncronize to be able to use the VCL

Example coming....

Shane
procedure TCombinationThread.DoSomething;
begin
 FindCombinations(FValue, FMin, FLevel);
 Form1.Label1.Caption:='testing'; // for example
 ExitThread(0);
end;

procedure TCombinationThread.Execute;
begin
  while not terminated do
  begin
     Synchronize(DoSomeThing);
   end;
end;


Shane
Avatar of emu10k1

ASKER

but doing this, it will call the function findocmbinations a lot of times, and I want to call this function only one time.
Avatar of emu10k1

ASKER

but doing this, it will call the function findcombinations a lot of times, and I want to call this function only one time.
Your question was that you "can't access any component of main form"

The solution was the syncronization.

Note:

When the execute method is ran, it runs through the entire execute method. It is not a timer. It is not called over and over like the OnTImerEvent. Unless of course you call it from an external source (like a timer) over and over again.

Im not sure what  FindCombinations does, but unless it has a repeat until or somekind of loop telling it to repeat, it should be only called once

Shane
Never mind (Duh, i got the FindCombinations).

It looks like its called recursively here...

  if (Level > 0) then begin
    for I := Min to 25 do
      if frmGerarCombinacao.verifica(I) then
        FindCombinations(Format('%s- %2.2d ', [Value, I]), I + 1, Level - 1); ************


Shane
Avatar of emu10k1

ASKER

so, What I have to do to solve this problem?
Can you explain to me in layman terms what your app does, and what this
method below does, i might be able to come up with a solution

if (Level > 0) then begin
    for I := Min to 25 do
      if frmGerarCombinacao.verifica(I) then
        FindCombinations(Format('%s- %2.2d ', [Value, I]), I + 1, Level - 1); ************


Shane
Avatar of emu10k1

ASKER

function TfrmGerarCombinacoes.verifica(Numero: Integer): boolean;
begin
  if Lista.IndexOf(IntToStr(Numero)) >= 0 then result := true
  else result := false;
end;

verifica will check if the number I is in the StringList (Lista) and if Yes, call the function again with I+1 and Level-1, it will generate a combination of number max range (1-25), but only using the numbers at Lista....

it will result something like this:

01-02-03-04-05-06-07-08-09-10-11-12-13-14-15
01-02-03-04-05-06-07-08-09-10-16-17-18-19-20
01-02-03-04-05-06-07-08-09-10-21-22-23-24-25
01-02-03-04-05-06-07-08-09-14-15-18-19-22-23
01-02-03-04-05-06-07-09-12-13-15-18-22-24-25
01-02-03-04-05-06-07-10-13-16-18-19-21-24-25
01-02-03-04-05-06-07-11-12-13-16-17-18-21-22
01-02-03-04-05-06-07-11-12-13-19-20-23-24-25
01-02-03-04-05-06-07-14-15-16-17-18-23-24-25
01-02-03-04-05-06-08-11-14-15-16-19-20-21-22
01-02-03-04-05-06-08-12-14-16-18-21-22-24-25
01-02-03-04-05-07-09-12-14-15-17-19-20-21-23
01-02-03-04-05-07-10-13-14-15-18-19-20-22-24
01-02-03-04-05-07-11-14-15-18-20-21-22-23-25
01-02-03-04-05-08-09-11-12-14-16-17-19-24-25
01-02-03-04-05-08-09-11-12-15-18-20-22-23-24
01-02-03-04-05-08-09-13-14-15-17-18-21-22-25
01-02-03-04-05-08-10-11-13-14-16-18-20-23-25
Something like:

constructor TCombinationThread.Create(v: string; m, l: integer);
begin
  inherited Create(true);
  FList.TStringList.Create;
  FValue := v;
  FMin := m;
  FLevel := l;
  FreeOnTerminate := true;
  Resume;
end;

destructor TCombinationThread.Destroy;
begin
  FList.Free;
  inherited;
end;

procedure TCombinationThread.SetALabel;
begin
   Form1.Label1.Caption:='testing'; // for example
end;

procedure TCombinationThread.ResetALabel;
begin
   Form1.Label1.Caption:='finished'; // for example
end;

procedure TCombinationThread.SetResult;
begin
   Form1.Memo1.Lines.Text := FList.Text; // for example
end;

procedure TCombinationThread.Execute;
begin
  Synchronize(SetALabel);
  Application.ProcessMessages;
  FindCombinations(FValue, FMin, FLevel);
  Synchronize(ResetALabel);
  Synchronize(SetResult);
end;

and use it like:

AThread := TCombinationThread.Create('name', 5, 10);

Regards, Geo
Looks like you will have to break apart your code, and create a few more methods which will need to be called in the Synchronize....

creating an example

Shane
Never mind, looks like Geo has found the solution for ya...

Shane
Avatar of emu10k1

ASKER

geobul: I try your code but the error it shows me the same error message at "Form1.Label1.Caption:='testing'; // for example"
Avatar of emu10k1

ASKER

sorry, it shows me that same error message at "Form1.Label1.Caption:='testing';" line.
I believe he knew that already!


Shane
Avatar of emu10k1

ASKER

I think so, Shane :)
Avatar of emu10k1

ASKER

I know what is happening... when I put this form (mdi child) to auto-create, it works fine, but when I delete this form from auto-create form's list, and use for example,
"  TfrmGerarCombinacoes.Create(Self);" to create the form, it generate the error message.
ahhhhhhhhh, you didn't say anything about any MDI forms...... that was an important aspect, seeing they dont behave 100 % like standard forms


Shane
try

 TfrmGerarCombinacoes.Create(Application);"
Avatar of emu10k1

ASKER

More one thing, If I replace "TfrmGerarCombinacoes.Create(Self);" tp  "Application.CreateForm(TfrmGerarCombinacao, frmGerarCombinacao);" it works fine, I can delete from auto-create form's list, but I can only use one form, If I create 2 forms and click on FindCombinations one time at 2 forms, it will generate an error, if I click at first, and when the first end, I click at second it works fine, but the 2 forms at same time generate an error...

what I have to do? What the better solution for this?

thanks
I dont see any of your code here for creating and then releasing the forms, can you show it to us please

Shane
Avatar of emu10k1

ASKER

to create I've tried "TfrmGerarCombinacoes.Create(Application);" and "TfrmGerarCombinacoes.Create(Self);"  and they generate an error message.
When I use "Application.CreateForm(TfrmGerarCombinacao, frmGerarCombinacao);" it works, but at only first form, If I open 2 forms, it generate an error when I try to use the thread at second form.

and onClose, I'm using "Action := CaFree;"

procedure TfrmGerarCombinacoes.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := CaFree;
end;
Your saying - "Application.CreateForm(TfrmGerarCombinacao, frmGerarCombinacao); does not cause an error. but TfrmGerarCombinacoes.Create(Application); does

AGAIN , can we see the code where this is!

TfrmGerarCombinacoes.Create(Application);

Shane
Avatar of emu10k1

ASKER

procedure TfrmLotoFacil.Combinar1Click(Sender: TObject);
begin
  TfrmGerarCombinacoes.Create(Application);
  //Application.CreateForm(TfrmGerarCombinacao, frmGerarCombinacao);
  //TfrmGerarCombinacoes.Create(Self);
end;

I try create(Application) it generate an error when the thread try to access the mdi form, create(Self) too, and Application.CreateForm works ( the thread can access the mdi form).
You need a variable

 var
 MyForm: TfrmGerarCombinacoes;

 MyFom:= TfrmGerarCombinacoes.Create(Application);


Shane
Avatar of emu10k1

ASKER

I try this too... the same error...
Then there is something else we haven't seen for code

if the AutoCreate form works, the RunTIme Creation should work as well

Can you send me your code , so as i can see it all -

holmesshane AT charter DOT net

Shane
Avatar of kretzschmar
>it works fine, but when I delete this form from auto-create form's list,
>and use for example,
>"  TfrmGerarCombinacoes.Create(Self);" to create the form, it
>generate the error message.

you need a reference to be forwarded to your thread,
and in the thread remove formX from the calls

to doing so, you can overload the thread-constructor

just ask, if you need details

meikl ;-)
ASKER CERTIFIED SOLUTION
Avatar of geobul
geobul

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
There are other more complicated ways for doing the same without using Synchronize like:
- using a critical section for accessing a shared resource (the label's caption in your case). You have to access it via the same critical section in the all threads and in the main VCL thread (i.e. in your forms).
- the thread can send a custom message to a form, the form then must handle that message and change the value of the label's caption in the message handler.

Regards, Geo

   type
     TCombinationThread = class(TThread)
   private
     Owner_Form : TForm;          // here you will take Form
   public
     constructor Create (OwnerHandle : THandle; .....your params here ...)
   end;

  constructor TCombinationThread.Create(Owner : TForm; .....your params here ...)
  begin
    ...
    Owner_Form := Owner;
    ...
  end;

  // now you have the handle of the form in the thread
  // in my opinion this is all you need.

  you can access the label like this :
 
  (Owner.FindComponent('Label1') as TLabel).Caption := 'testing';
 
 
Avatar of emu10k1

ASKER

Thanks geobul, it works now.
:-)