Article View: borland.public.cppbuilder.nativeapi
Article #5856Re: Creating a TForm from a Tthread.
From: Jermey
Date: Mon, 27 Jun 2005 18:45
Date: Mon, 27 Jun 2005 18:45
232 lines
9539 bytes
9539 bytes
Thank you so much!
I think using the TMessage to update the mainThread's GUI from the
worker Thread is very smart. I never thought of that before. I learned
something everyday.
For
>>I want these TForms work like individual executable.
If I make the mainForm spawn a new Executable for each device, I don't
need to worry about the Thread problem. I just let the OS handle everything.
Jer
JD wrote:
> 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:
<42c04516$1@newsgroups.borland.com>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news4.google.com!news.glorb.com!border1.nntp.dca.giganews.com!border2.nntp.dca.giganews.com!nntp.giganews.com!diablo.voicenet.com!news-out.newsgroups.com!propagator2-sterling.newsfeeds.com!in.nntp.be!caladan.landsraad.net!newsgroups.borland.com!not-for-mailReferences:
<42bc9a02$1@newsgroups.borland.com> <42bd47e0$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 Date: Mon, 27 Jun 2005 11:45:16 -0700 From: Jermey <jasonssxxx@hotmail.com> User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.2) Gecko/20040804 Netscape/7.2 (ax) X-Accept-Language: en-us, en MIME-Version: 1.0 Newsgroups: borland.public.cppbuilder.nativeapi Subject: Re: Creating a TForm from a Tthread. References: <42bc9a02$1@newsgroups.borland.com> <42bd47e0$1@newsgroups.borland.com> In-Reply-To: <42bd47e0$1@newsgroups.borland.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 8bit NNTP-Posting-Host: 12.44.185.2 Message-ID: <42c04516$1@newsgroups.borland.com> X-Trace: newsgroups.borland.com 1119896854 12.44.185.2 (27 Jun 2005 11:27:34 -0700) Lines: 232 Path: arc1-ca.pugleaf.net!g2news1.google.com!news4.google.com!news.glorb.com!border1.nntp.dca.giganews.com!border2.nntp.dca.giganews.com!nntp.giganews.com!diablo.voicenet.com!news-out.newsgroups.com!propagator2-sterling.newsfeeds.com!in.nntp.be!caladan.landsraad.net!newsgroups.borland.com!not-for-mail