Solved

Problem with TThread

Posted on 2004-04-06
34
515 Views
Last Modified: 2010-04-05
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.
0
Comment
Question by:emu10k1
  • 14
  • 14
  • 4
  • +2
34 Comments
 

Author Comment

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

please, how I can solve this problem?
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10767356
You have to syncronize to be able to use the VCL

Example coming....

Shane
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10767379
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
0
 

Author Comment

by:emu10k1
ID: 10767451
but doing this, it will call the function findocmbinations a lot of times, and I want to call this function only one time.
0
 

Author Comment

by:emu10k1
ID: 10767455
but doing this, it will call the function findcombinations a lot of times, and I want to call this function only one time.
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10767526
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
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10767546
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
0
 

Author Comment

by:emu10k1
ID: 10767822
so, What I have to do to solve this problem?
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10767878
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
0
 

Author Comment

by:emu10k1
ID: 10768082
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
0
 
LVL 17

Expert Comment

by:geobul
ID: 10768148
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
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10768196
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
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10768201
Never mind, looks like Geo has found the solution for ya...

Shane
0
 

Author Comment

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

Author Comment

by:emu10k1
ID: 10768317
sorry, it shows me that same error message at "Form1.Label1.Caption:='testing';" line.
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10768350
I believe he knew that already!


Shane
0
 

Author Comment

by:emu10k1
ID: 10768384
I think so, Shane :)
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 

Author Comment

by:emu10k1
ID: 10769042
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.
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10769104
ahhhhhhhhh, you didn't say anything about any MDI forms...... that was an important aspect, seeing they dont behave 100 % like standard forms


Shane
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10769122
try

 TfrmGerarCombinacoes.Create(Application);"
0
 

Author Comment

by:emu10k1
ID: 10769147
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
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10769177
I dont see any of your code here for creating and then releasing the forms, can you show it to us please

Shane
0
 

Author Comment

by:emu10k1
ID: 10769332
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;
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10769372
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
0
 

Author Comment

by:emu10k1
ID: 10769442
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).
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10769475
You need a variable

 var
 MyForm: TfrmGerarCombinacoes;

 MyFom:= TfrmGerarCombinacoes.Create(Application);


Shane
0
 

Author Comment

by:emu10k1
ID: 10769511
I try this too... the same error...
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10769525
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
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 10772098
>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 ;-)
0
 
LVL 17

Accepted Solution

by:
geobul earned 500 total points
ID: 10772390
Hi,

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

Do you have Form1 in your project and Label1 on it? If not then replace them with actual form and label names. If you want to run the thread many times simultaneously on different instances of a form then, as kretzschmar already said, you'll need a thread private variable of TYourForm type. Add one more parameter to Create method and set the private variable. Use this variable in the other methods:

TCombinationThread = class(TThread)
  private
    FValue: String;
    FMin, FLevel: Integer;
    FList: TStrings;
    FForm: TfrmGerarCombinacoes
    ...

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

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

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

....

Regards, Geo
0
 
LVL 17

Expert Comment

by:geobul
ID: 10772434
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
0
 
LVL 12

Expert Comment

by:Ivanov_G
ID: 10772803

   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';
 
 
0
 

Author Comment

by:emu10k1
ID: 10773750
Thanks geobul, it works now.
0
 
LVL 17

Expert Comment

by:geobul
ID: 10774316
:-)
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now