Board index » cppbuilder » Re: Passing info between threads

Re: Passing info between threads


2007-02-22 04:34:49 PM
cppbuilder27
Hi,
I think you're nearly there but just a few things:
Quote
Then I have:
TThreadList* SctpCommandList;
defined globally in my main unit (and extern in the thread unit), which
gets instantiated with 'new' in my main form's constructor.
Does MainFrom need to know about the command list or its workings? You can
make the threadlist a member of the thread, not the form, add a
AddCommand(SctpCmdParams) method to the thread which does
SctpCommandList->Add(SctpCmdParams), or go one step further and make the
parameters of that method the fields you need to pass to the thread that
need adding to 'its' queue. That way you can change the entire queueing
system in the thread and the main form would not know or be affected.
Quote
SctpCommandList->Add(SctpCmdParams);
sctpThread->HandleCommand();
I don't think this helps your performance or queueing. This adds the
command to the list in the MainForms scope, and also does the operation of
processing the queue in the main forms scope. The HandleCommand needs to be
done via the threads execute method to make the thread do the work, the main
form needs to add and then signal the thread. If you want to get the
principle of the queue processing working initially you can just get the
thread execute to Sleep(10ms) and then check if the queue is non-empty in
its while loop.
Hope this helps.
Paul
 
 

Re:Re: Passing info between threads

"Paul at NCF" < XXXX@XXXXX.COM >wrote in message
Quote


Does MainForm need to know about the command list or its workings? You
can make the threadlist a member of the thread, not the form, add a
AddCommand(SctpCmdParams) method to the thread which does
SctpCommandList->Add(SctpCmdParams), or go one step further and make the
parameters of that method the fields you need to pass to the thread that
need adding to 'its' queue. That way you can change the entire queuing
system in the thread and the main form would not know or be affected.
Wouldn't that defeat the purpose? In order for the queue to protect the two
threads from each other, it needs to bridge the gap between the two. If my
thread puts the message into the queue itself, and then later takes it off,
then what's the queue there for?
Quote
>SctpCommandList->Add(SctpCmdParams);
>sctpThread->HandleCommand();

I don't think this helps your performance or queuing. This adds the
command to the list in the MainForms scope, and also does the operation of
processing the queue in the main forms scope.
That looks to me like it adds the command in the main form, but then calls
the sctp thread to handle it.
Quote
the main form needs to add and then signal the thread.
Isn't that just what it's doing here? The first line above adds the
command, then the second line tells the thread there's a command for it to
handle.
Quote
The HandleCommand needs to be done via the threads execute method to make
the thread do the work,
If you want to get the principle of the queue processing working initially
you can just get the thread execute to Sleep(10ms) and then check if the
queue is non-empty in its while loop.
I don't want the thread to sleep, because some of the applications I use
this for may be handling high traffic volumes, and I don't want to slow down
performance by blocking the communication library. The while loop in the
thread's Execute function looks like this right now:
while (! Terminated ) {
if (sctp_eventLoop() < 0)
break ;
}
If I could easily check whether there's anything in the queue, then maybe I
could add a line here (so that it alternates with the library's
sctp_eventLoop function) to check for any commands it has pending. However,
even seeing whether the TThreadList is empty or not requires locking it
first. If this thread keeps continually locking the list (even when there's
nothing there), would the main thread have a harder time getting its own
lock when it wants to add something? I prefer for the main unit to tell the
thread when there's a command in the list for it to handle. That way, the
thread would only need to lock the list when it's retrieving a command off
of it.
 

Re:Re: Passing info between threads

Hi,
I may have lost the plot, but I thought you wanted to pass information to a
(secondary) thread from the main VCL thread (mainform)?
Quote
Wouldn't that defeat the purpose? In order for the queue to protect the
two threads from each other, it needs to bridge the gap between the two.
If my thread puts the message into the queue itself, and then later takes
it off, then what's the queue there for?
If you call the threads AddCommand method to add to the queue, the main VCL
thread is running and calling into the thread. So the VCL thread is adding
to the queue. The thread could then get signaled and woken up, and remove
the item off the queue.
Quote
That looks to me like it adds the command in the main form, but then calls
the sctp thread to handle it.
Yes, thats what the example looked like that you showed?
Quote
Isn't that just what it's doing here? The first line above adds the
command, then the second line tells the thread there's a command for it to
handle.
Not sure it did. Looked to me like the VCL thread added the command to the
queue, and then calls the threads handle command to take it off the queue
and process it?
Quote
I don't want the thread to sleep, because some of the applications I use
this for may be handling high traffic volumes, and I don't want to slow
down performance by blocking the communication library. The while loop in
the thread's Execute function looks like this right now:
Yes, I wouldn't do that normally, I was suggesting it to get something
basically working without setting up the signalling and wait for objects
API.
Quote
If I could easily check whether there's anything in the queue, then maybe
I could add a line here (so that it alternates with the library's
sctp_eventLoop function) to check for any commands it has pending.
However, even seeing whether the TThreadList is empty or not requires
locking it first. If this thread keeps continually locking the list (even
when there's nothing there), would the main thread have a harder time
getting its own lock when it wants to add something? I prefer for the
main unit to tell the thread when there's a command in the list for it to
handle. That way, the thread would only need to lock the list when it's
retrieving a command off of it.
WaitForMultipleObjects would only wake up the thread when something was
signalled. And it would only block the VCL thread when it was locked, and
vv.
As I said, I may have lost the plot and be completely misunderstanding what
you are trying to do.
Paul
 

{smallsort}

Re:Re: Passing info between threads

"Paul at NCF" < XXXX@XXXXX.COM >wrote in message
Quote

I may have lost the plot, but I thought you wanted to pass information to
a (secondary) thread from the main VCL thread (mainform)?
Yes.
Quote
So the VCL thread is adding to the queue.
Yes.
Quote
The thread could then get signaled and woken up, and remove the item off
the queue.
Yes. That's what the HandleCommand is there for, and why it's in the
sctpThread.
Quote
Not sure it did. Looked to me like the VCL thread added the command to
the queue, and then calls the threads handle command to take it off the
queue and process it?
Isn't that what you just said it should do?
Quote
WaitForMultipleObjects would only wake up the thread when something was
signalled. And it would only block the VCL thread when it was locked
But I can't have the thread sleeping or waiting or anything else that would
keep it from processing the library's event loop. It might be a while
before the next time the main unit initiates an outgoing message, but in the
meantime, the library still needs to keep looking for incoming messages. It
relies on sctp_eventLoop() being called continuously. Whatever I add into
the Execute's while loop has to co-exist peacefully with:
while (! Terminated ) {
if (sctp_eventLoop() < 0)
break ;
}
and not block that eventLoop from happening.
 

Re:Re: Passing info between threads

"Lesley Anne" wrote:
Quote
"Paul at NCF" wrote in message
>
>The thread could then get signaled and woken up, and remove the item off
>the queue.

Yes. That's what the HandleCommand is there for, and why it's in the
sctpThread.
Yes, it is a part of the Thread Class, but it is being called by the
VCL Thread. It is being executed by the VCL thread, not the sctp
thread. The Thread::Execute() needs to be calling the HandleCommand
code when data becomes available to send.
Quote
>Not sure it did. Looked to me like the VCL thread added the command to
>the queue, and then calls the threads handle command to take it off the
>queue and process it?

Isn't that what you just said it should do?
No, it should _signal_ the thread to send the data, not _call_ the
thread function that does the actual work.
Quote
>WaitForMultipleObjects would only wake up the thread when something was
>signalled. And it would only block the VCL thread when it was locked

But I can't have the thread sleeping or waiting or anything else that would
keep it from processing the library's event loop.
Why do you have an event loop? I thought you were using blocking
calls.
Quote
It might be a while
before the next time the main unit initiates an outgoing message, but in the
meantime, the library still needs to keep looking for incoming messages. It
relies on sctp_eventLoop() being called continuously. Whatever I add into
the Execute's while loop has to co-exist peacefully with:
void __fastcall TForm1::Send(String Message, int Site)
{
if (sctpThread) {
TSctpCmdParams *SctpCmdParams = new TSctpCmdParams;
SctpCmdParams->Command = scmdSend;
SctpCmdParams->AssocID = associationID[Site];
strcpy(SctpCmdParams->MsgBuffer, Message.c_str());
SctpCmdParams->MsgLen = Message.Length();
SctpCommandList->Add(SctpCmdParams);
evSendEvent->SetEvent( ); // <<<---
} else
Memo1->Lines->Add("ERROR: sctp Thread does not exist");
}
....... ....... TsctpThread::Execute( ..... )
{
/* setup the thread and sending socket info */
/* not shown */
Quote
while (! Terminated ) {
// no delay or locks
// acts like non-existant if( evSendEvent->Signaled )
if( evSendEvent->WaitFor( 0 ) == wrSignaled )
{
// Only Locks when Signaled
TList* List = SctpCommandList->LockList();
if( List->Count>0 )
{
TSctpCmdParams *SctpCmdParams =
(TSctpCmdParams*)List->First();
List->Delete( 0 );
SctpCommandList->UnlockList();
HandleCommand( SctpCmdParams );
Quote
if (sctp_eventLoop() < 0)
break ;
// In case 2 events came in and we missed the first
evSendEvent->SetEvent( );
continue; // skip the sleep()
}else{
// no command listed, probably got here
// from the above SetEvent();
SctpCommandList->UnlockList();
};
};
Quote
if (sctp_eventLoop() < 0)
break ;
// give up time slice so other threads can do something.
// since we have nothing to do right now.
Sleep( 0 );
Quote
}
/* Socket and Thread cleanup */
/* not shown */
}
Quote
and not block that eventLoop from happening.
I'm not sure what sctp_eventLoop is supposed to do for you.
In my stuff, I have One thread for Sending, and a Second thread for
Receiving.
In the Send thread, I WaitForSingleObject( hSendEvent, 1000 )
Which wakes up every second and checks for termination.
Otherwise, it sits using no CPU.
When Send request comes in, it does a blocking send, and checks for
another request before resuming the Wait. The App puts up a request,
then Signal( hSendEvent )
On the Application side, I have the same setup, blocking for a Receive
event that the Receive Thread sends when data becomes available.
The Receive Thread is mostly blocked on the Receive().
The Send Thread is mostly blocked on the WaitForSingleObject()
 

Re:Re: Passing info between threads

Hi,
I was going to send an example this morning, but looks like Bob has beat me
to it. However as a bit more help:
I use a set of classes that I've built up to standardise what I want my
threads/tasks to do for me and my fellow team members. It may not be
perfect, but I've yet to make any changes to it in the last year or so.
I've built up all the other classes for sockets and serial comms around
this.
Hope this might help.
Paul
__fastcall CTask::CTask( char * name_string,
ulong identity,
ulong priority )
: TThread(true)
{
// make sure thread is deleted by owner
FreeOnTerminate = false;
// initialise member variables
MyHandlesCount = 0;
MyHandles = NULL;
MyEvent = NULL;
// create event and store handle
// unnamed os object to avoid unnecessary issues!!
MyEvent = new TEvent ( NULL, false, false, "" );
MyHandles[MyHandlesCount++] = (HANDLE) MyEvent->Handle;
}
void
CTask::WakeUpSignal( void )
{
MyEvent->SetEvent();
}
bool
CTask::WaitForWakeUp( int wait_time )
{
bool result = false;
ulong apilimit = MyHandlesCount - 1;
WMOresult = WaitForMultipleObjects( MyHandlesCount, MyHandles, false,
wait_time );
// test for API failure code
if( (WMOresult>= WAIT_OBJECT_0)
&& (WMOresult <= (WAIT_OBJECT_0 + apilimit))
){
result = false;
}
else
if( (WMOresult>= WAIT_ABANDONED_0)
&& (WMOresult <= (WAIT_ABANDONED_0 + apilimit))
){
result = true;
}
else
if (WMOresult == WAIT_TIMEOUT)
{
// timeout waiting for event
result = true;
}
else
{
int i = GetLastError();
result = true;
}
return result;
}
void Controller::SendMessageToTask( SomeMessageType * message )
{
// list type hidden from task owner, it doesn't need to know
if (MyMbx)
{
// mailbox signals the task event when adding a message
MyMbx->AddMailMsg(message);
}
}
void Controller::Execute()
{
while (!Terminated)
{
// wait for wake up if nothing to process
if (MyMbx->GetMailMsg() == NULL)
{
result = WaitForWakeUp( 100 );
}
etc.....
}
 

Re:Re: Passing info between threads

Lesley
data is stored in a csv type file format.
the main vcl thread updates these files with writeeclisive.
then post a message to the thread to update its data.
so the thread is busy. only interrupted if some additional or current data
is changed
sctp_eventLoop may need to periodically check the message que ?
while (! Terminated ) {
if (sctp_eventLoop() < 0)
break ;
if(!DoThreadMessage( ) )
break;
}
bool __fastcall TMonitorThread::DoThreadMessage(void)
{
if (Terminated) return False;
MSG msg;
ZeroMemory (&msg, sizeof (MSG));
if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE ))
{
switch(msg.message)
{
case WM_QUIT :
Terminate();
return False;
case TM_CLIENTCHANGED :
RenewItemInfo(msg.lParam);
break;
(...) etc
}
}
return True;
}
"Lesley Anne" wrote in message
Quote
But I can't have the thread sleeping or waiting or anything else that
would keep it from processing the library's event loop. It might be a
while before the next time the main unit initiates an outgoing message,
but in the meantime, the library still needs to keep looking for incoming
messages. It relies on sctp_eventLoop() being called continuously.
Whatever I add into the Execute's while loop has to co-exist peacefully
with:

while (! Terminated ) {
if (sctp_eventLoop() < 0)
break ;
}

and not block that eventLoop from happening.