🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.cppbuilder.nativeapi
Article #5856

Re: Creating a TForm from a Tthread.

#5856
From: Jermey
Date: Mon, 27 Jun 2005 18:45
232 lines
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-mail
References: <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