Article View: borland.public.cppbuilder.nativeapi
Article #5923Re: Creating a TForm from a Tthread.
From: JD
Date: Sat, 25 Jun 2005 12:02
Date: Sat, 25 Jun 2005 12:02
208 lines
8424 bytes
8424 bytes
Jermey <jasonssxxx@hotmail.com> wrote:
>
> [...] So I put the calculation in a Thread and added a timer
> to refresh and update the display with the latest data.
This is a good place for a thread but using a TTimer is not a
good idea and just the fact that you are using a TTimer leads
me to suspect that the code you have to update the GUI is not
thread safe.
To be clear, *all* GUI updating *must* be performed within the
context of the main thread - period. If not, sooner or later,
you'll encounter concurrent access and the program will crash.
TThread has a Syncronize method which causes a specified block
of code to be executed within the context of the main thread
so if the thread is updating the GUI, it should be using
Syncronize to so. The problem that I have with Syncronize is
that the thread is blocked while the the sync'ed block of code
executes. There is another way to update the GUI within the
context of the main thread and not block the worker thread in
the process and it does not need a TTimer because the thread
causes the GUI update as soon as it's ready.
Start by adding a new header file to the project. In this
header, define a structure that represents the data that the
worker thread will be passing to the form:
//----------------------------�------------------------------�---
#ifndef DataPacketH
#define DataPacketH
//----------------------------�------------------------------�---
struct TDataPacket
{
....
};
//----------------------------�------------------------------�---
#endif
Then modify the form's header:
//----------------------------�------------------------------�---
#ifndef Unit1H
#define Unit1H
//----------------------------�------------------------------�---
#include <Classes.hpp>
....
//----------------------------�------------------------------�---
#include <vector>
#include "DataPacket.h"
//----------------------------�------------------------------�---
class TForm1 : public TForm
{
__published:
....
protected:
virtual void __fastcall WndProc( TMessage &Message );
private:
#define UWM_THREAD_UPDATE (WM_USER + 100)
typedef TForm inherited;
TDataPacket ADataPacket;
std::vector<TDataPacket>vDataP�acket;
void __fastcall SetDataPacket( TDataPacket );
void __fastcall ProcessThreadPacket();
public:
__fastcall TForm1(TComponent* Owner); // <-- don't duplicate
__fastcall ~TForm1();
__property TDataPacket DataPacket = { write = SetDataPacket };
};
//----------------------------�------------------------------�---
extern PACKAGE TForm1 *Form1;
//----------------------------�------------------------------�---
#endif
Next, in the form's unit:
// includes for TCriticalSection
#include <SyncObjs.hpp>
// global TCriticalSection - read the help - one instance per application
TCriticalSection *CSection = NULL;
// arbitrary number that's up to you
int MaxVectorEntries = 25;
//----------------------------�------------------------------�---
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// allocate the TCriticalSection
CSection = new TCriticalSection();
// pre allocate the vector
vDataPacket.reserve( MaxVectorEntries );
}
//----------------------------�------------------------------�---
__fastcall TForm1::~TForm1()
{
delete CSection;
}
//----------------------------�------------------------------�---
void __fastcall TForm1::WndProc(TMessage &Message)
{
if( Message.Msg == UWM_THREAD_UPDATE )
{
ProcessThreadPacket();
}
inherited::WndProc( Message );
}
//----------------------------�------------------------------�---
void __fastcall TForm1::SetDataPacket( TDataPacket Packet )
{
CSection->Enter(); // prevent concurrent access to the data packets
vDataPacket.push_back( Packet ); // record the new packet
::PostMessage( Handle, UWM_THREAD_UPDATE, 0, 0 ); // add to the form's todo list
if( vDataPacket.size() == MaxVectorEntries )
{
Application->ProcessMessages()�; // process messages if there are too many pending thread packets
}
CSection->Leave();
}
//----------------------------�------------------------------�---
void __fastcall TForm1::ProcessThreadPacket()
{
CSection->Enter(); // prevent concurrent access pf data packets
ADataPacket = vDataPacket[ 0 ]; // get a packet
vDataPacket.erase( vDataPacket.begin() ); // remove the packet from the list
CSection->Leave();
//****************************�***
//
// Process the packet here
//
//****************************�***
}
//----------------------------�------------------------------�---
The last thing that you have to do is modify your thread to
include a definition for a TDataPacket and a pointer of type
TForm1 (or what ever you named the form) and work out some
method of assigning that pointer to point to the above form.
The easiest way is to pass the form into the threads'
constructor. Add the code below and change your allocation of
the thread to include the above form as a parameter:
TMyThread *pThread = new TMyThread( Form1 );
or even better if the thread is allocated from the above form:
TMyThread *pThread = new TMyThread( this );
//--- in the threads header ------------------------------�-----
#include "DataPacket.h"
#include "Form1.h"
private:
TForm1 *FForm;
TDataPacket FPacket;
public:
__fastcall TMyThread( TForm1* );
//--- in the unit ------------------------------�---------------
__fastcall TMyThread::TMyThread( TForm1 *pForm ) : TThread( true )
{
FForm = pForm;
Resume();
}
//----------------------------�------------------------------�---
void __fastcall TMyThread::Execute()
{
while( !Terminated )
{
//****************************�*
//
// do something to fillup the data packet
//
//****************************�*
FForm->DataPacket = FPacket; // pass the data to the form
}
}
//----------------------------�------------------------------�---
> Is it possible to create a TForm inside the Thread?
Gambit is correct but to elaborate on what he said, It is
possible for a worker thread to cause a form to be created
but it is not possible to create the form within the context
of the worker thread.
For example, if the worker thread is created suspended, you
can dynamically allocate the form in the thread's constructor
*before* you Resume the thread because that part of the thread
is executed within the context of the main thread. You can
also use a win32 API to send a custom message back to the main
thread which will then dynamically allocate the form.
> I want these TForms work like individual executable.
I don't think that this statement means the same to you as it
does to me. Please elaborate.
> I don't want if one TForm is hung, the rest of TForms will
> hang also.
That's what threads are for - to isolate cpu intensive code
from the main thread so that the GUI remains responsive.
~ JD
Technical Details
Message-ID:
<42bd47e0$1@newsgroups.borland.com>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news4.google.com!news2.google.com!proxad.net!213.200.89.82.MISMATCH!tiscali!newsfeed1.ip.tiscali.net!newsfeed00.sul.t-online.de!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mailReferences:
<42bc9a02$1@newsgroups.borland.com>Show Full Headers
Complete Article Headers
X-Google-Thread: 10cc67,23ba61d084da5a10 X-Google-Attributes: gid10cc67,public X-Google-Language: ENGLISH,ASCII From: "JD" <nospam@nospam.com> Sender: "JD" <nospam@nospam.com> Reply-To: "JD" <nospam@nospam.com> Subject: Re: Creating a TForm from a Tthread. Newsgroups: borland.public.cppbuilder.nativeapi X-User-Info: 64.12.116.131 X-Forwarded-For: 172.167.192.100 References: <42bc9a02$1@newsgroups.borland.com> NNTP-Posting-Host: 207.105.83.66 Message-ID: <42bd47e0$1@newsgroups.borland.com> Date: 25 Jun 2005 05:02:40 -0700 X-Trace: newsgroups.borland.com 1119700960 207.105.83.66 (25 Jun 2005 05:02:40 -0700) Lines: 209 Path: arc1-ca.pugleaf.net!g2news1.google.com!news4.google.com!news2.google.com!proxad.net!213.200.89.82.MISMATCH!tiscali!newsfeed1.ip.tiscali.net!newsfeed00.sul.t-online.de!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail