 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
iwl Guest
|
Posted: Mon Dec 11, 2006 4:39 pm Post subject: VCL-Component-Acces from Thread |
|
|
Hello,
I have to acces several Visual components from a thread (e.g.
Trackbars)
Trackbar->Postition=xyz etc.
As far I red this is not safe.
Using syncronize is very unconfortable for me, when I have to write a
func for every thing I wan't to do passable to Syncronize.
Is there some bether way something I can call befor
Trackbar->Postition=xyz and after that?
I read some thing about Critical section but as far I understand this
only works also having such a section in the VCL-main-thread I can't
change. |
|
| Back to top |
|
 |
Danzer Guest
|
Posted: Mon Dec 11, 2006 9:30 pm Post subject: Re: VCL-Component-Acces from Thread |
|
|
iwl wrote:
| Quote: | Hello,
I have to acces several Visual components from a thread (e.g.
Trackbars)
Trackbar->Postition=xyz etc.
As far I red this is not safe.
Using syncronize is very unconfortable for me, when I have to write a
func for every thing I wan't to do passable to Syncronize.
Is there some bether way something I can call befor
Trackbar->Postition=xyz and after that?
I read some thing about Critical section but as far I understand this
only works also having such a section in the VCL-main-thread I can't
change.
|
Post a message in your thread to the owner of the Trackbar and call
Application->ProcessMessages. Update the Trackbar in the message handler.
Danzer |
|
| Back to top |
|
 |
Clayton Arends Guest
|
Posted: Tue Dec 12, 2006 12:23 am Post subject: Re: VCL-Component-Acces from Thread |
|
|
| Quote: | As far I red this is not safe.
|
That is correct. VCL controls are not thread-safe.
| Quote: | I read some thing about Critical section but as far I understand this
only works also having such a section in the VCL-main-thread I can't
change.
|
As you have mentioned TCriticalSection will not work for updating VCL
controls from a thread. The only way to update a VCL control is in the
context of the main thread.
To solve your problem you can use what Danzer suggested (though, you don't
need the Application->ProcessMessages() call). The Windows message queue is
thread-safe. If you'd like to get a little fancier try the following code:
// H file
#include <Classes.hpp>
#include <ComCtrls.hpp>
#include <Controls.hpp>
#include <Forms.hpp>
#include <StdCtrls.hpp>
#include <SyncObjs.hpp>
static const UM_UPDATEPROGRESSBAR = WM_USER + 100;
class TTestThread : public TThread
{
public:
struct TMessageData
{
unsigned Position;
unsigned Max;
};
private:
TCriticalSection* fDataSync;
TMessageData fMessageData;
HANDLE fOwnerHandle;
protected:
virtual void __fastcall Execute();
public:
TTestThread(HANDLE OwnerHandle);
__fastcall ~TTestThread();
TMessageData GetMessageData();
};
class TForm1 : public TForm
{
__published:
TProgressBar *ProgressBar1;
TButton *Button1;
void __fastcall Button1Click(TObject *Sender);
private:
TTestThread* Thread;
void __fastcall ThreadTerminate(TObject* Sender);
protected:
void UmUpdateProgressBar(TMessage& Message);
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(UM_UPDATEPROGRESSBAR, TMessage,
UmUpdateProgressBar)
END_MESSAGE_MAP(TForm)
public:
__fastcall TForm1(TComponent* Owner);
};
// CPP file
TTestThread::TTestThread(HANDLE OwnerHandle)
: TThread(true), fOwnerHandle(OwnerHandle)
{
fDataSync = new TCriticalSection;
}
__fastcall TTestThread::~TTestThread()
{
delete fDataSync;
}
void __fastcall TTestThread::Execute()
{
// Initialize the position and range of the progress
fDataSync->Acquire();
fMessageData.Position = 0;
fMessageData.Max = 100000;
fDataSync->Release();
::PostMessage(fOwnerHandle, UM_UPDATEPROGRESSBAR, 0, 0);
for (int index = 0; index < 100000; ++index)
{
// a little delay to keep things from happening all at once
// (Sleep takes too long)
for (int index = 0; index < 10000; ++index);
// Update the progress position
fDataSync->Acquire();
fMessageData.Position = index;
fDataSync->Release();
::PostMessage(fOwnerHandle, UM_UPDATEPROGRESSBAR, 0, 0);
}
}
TTestThread::TMessageData TTestThread::GetMessageData()
{
fDataSync->Acquire();
TMessageData result = fMessageData;
fDataSync->Release();
return result;
}
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (Thread)
return;
Thread = new TTestThread(Handle);
Thread->FreeOnTerminate = true;
Thread->OnTerminate = &ThreadTerminate;
Thread->Resume();
}
void __fastcall TForm1::ThreadTerminate(TObject* Sender)
{
Thread = NULL;
}
void TForm1::UmUpdateProgressBar(TMessage& Message)
{
if (!Thread)
{ // Thread is completed
ProgressBar1->Position = ProgressBar1->Max;
return;
}
{ // Remove any other queued UM_UPDATEPROGRESSBAR messages
MSG msg;
while (PeekMessage(&msg, Handle,
UM_UPDATEPROGRESSBAR, UM_UPDATEPROGRESSBAR, PM_REMOVE));
}
TTestThread::TMessageData data = Thread->GetMessageData();
ProgressBar1->Position = data.Position;
ProgressBar1->Max = data.Max;
}
The purpose of this code is to not block either thread when the progress bar
needs to update. It also removes excess duplicate messages from the queue
(there might be a more Windows way of doing that, I'm not sure). To test
this create a new application, add a button and a progress bar.
There are other ways you can pass data from the worker thread to the main
thread. If you have a lot of data that you do not want to get copied then
you can pass by pointer but you'll need to have two accessor methods. One
acquires the critical section and one releases it. Just make sure you sync
up the reads and writes using a TCriticalSection or a
TMultiReadExclusiveWriteSynchronizer and always make sure that VCL control
updates are performed in the context of the main application thread.
HTH,
- Clayton |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|