 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
LesleyAnne Guest
|
Posted: Tue Aug 30, 2005 6:11 pm Post subject: how to keep a thread alive without a CPU-hogging infinite lo |
|
|
Is there a simple way to keep a thread from reaching the end of its Execute
function until conditions set in other functions are complete? I have a
threaded application where most of the child-thread processing is taking
place in various interrupt functions or functions called from the parent
thread and I want the Execute function to wait for everything to be
complete. I can make it work with a simple loop of the form:
while (WaitingForResults) {
Application->ProcessMessages();
}
in which "WaitingForResults" is a thread-global boolean controlled in other
functions. The only problem is that this keeps constant processing going on
and uses 100% CPU for as long as the thread is active. Is there a better
way of doing this?
Lesley Anne
|
|
| Back to top |
|
 |
Chris Uzdavinis (TeamB) Guest
|
Posted: Tue Aug 30, 2005 7:16 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
"LesleyAnne" <labaker (AT) snetnsa (DOT) com> writes:
| Quote: | Is there a simple way to keep a thread from reaching the end of its Execute
function until conditions set in other functions are complete?
|
There is a synchronization mechanism known as a "condition variable".
It can be used to put one or more threads to sleep until a condition
is satisfied. Windows does not have a built-in condition variable,
but there are implementations available that are built from
synchronization primitives that are offered by windows.
The two that immediately come to mind are the ACE library and the
Boost.Threads library.
In a nutshell, a condition variable needs to be given a mutex. The
pattern is to lock the mutex, then perform a loop while the condition
is not met. Inside the loop, you wait on the condition variable
again, which blocks.
This way, some other thread can do some action that may change the
condition, and then "signal" the condition variable, which will wake
up a sleeping thread, who checks his condition and if it's true,
breaks out of the loop, and holds the lock. If he is woken up
prematurely, and the condition is not met, he waits again.
--
Chris (TeamB);
|
|
| Back to top |
|
 |
LesleyAnne Guest
|
Posted: Tue Aug 30, 2005 7:40 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
The problem is that I don't think I want the thread to go to sleep. I need
all the thread's interrupt functions to be working. How do I make all the
thread's other functions work, but just not advance through this particular
function.
(I've also had similar problems come up in single-threaded applications,
where I wanted one function to wait until things had happened in other
functions.)
"Chris Uzdavinis (TeamB)" <chris (AT) uzdavinis (DOT) com> wrote
| Quote: | "LesleyAnne" <labaker (AT) snetnsa (DOT) com> writes:
Is there a simple way to keep a thread from reaching the end of its
Execute
function until conditions set in other functions are complete?
There is a synchronization mechanism known as a "condition variable".
It can be used to put one or more threads to sleep until a condition
is satisfied. Windows does not have a built-in condition variable,
but there are implementations available that are built from
synchronization primitives that are offered by windows.
The two that immediately come to mind are the ACE library and the
Boost.Threads library.
In a nutshell, a condition variable needs to be given a mutex. The
pattern is to lock the mutex, then perform a loop while the condition
is not met. Inside the loop, you wait on the condition variable
again, which blocks.
This way, some other thread can do some action that may change the
condition, and then "signal" the condition variable, which will wake
up a sleeping thread, who checks his condition and if it's true,
breaks out of the loop, and holds the lock. If he is woken up
prematurely, and the condition is not met, he waits again.
--
Chris (TeamB);
|
|
|
| Back to top |
|
 |
Chris Uzdavinis (TeamB) Guest
|
Posted: Tue Aug 30, 2005 7:50 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
"LesleyAnne" <labaker (AT) snetnsa (DOT) com> writes:
| Quote: | The problem is that I don't think I want the thread to go to sleep. I need
all the thread's interrupt functions to be working. How do I make all the
thread's other functions work, but just not advance through this particular
function.
(I've also had similar problems come up in single-threaded applications,
where I wanted one function to wait until things had happened in other
functions.)
|
I'm not sure I follow you. Within a single thread, as long as a
function is running, the calling function is halted. Functions always
wait until things complete in other functions that they call.
What sort of interrupt functions are you talking about?
--
Chris (TeamB);
|
|
| Back to top |
|
 |
LesleyAnne Guest
|
Posted: Tue Aug 30, 2005 9:15 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
| Quote: | The problem is that I don't think I want the thread to go to sleep. I
need
all the thread's interrupt functions to be working. How do I make all
the
thread's other functions work, but just not advance through this
particular
function.
I'm not sure I follow you. Within a single thread, as long as a
function is running, the calling function is halted. Functions always
wait until things complete in other functions that they call.
What sort of interrupt functions are you talking about?
|
The functions it's waiting for are not functions that it calls directly
(except that Application->ProcessMessages will call them if there are any to
be handled). It's waiting for functions that may be called from another
thread (functions within a child thread that can be called by the parent
thread), or waiting for event handler functions (OnError, OnConnect, etc.).
|
|
| Back to top |
|
 |
Alex Bakaev [TeamB] Guest
|
Posted: Tue Aug 30, 2005 9:43 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
LesleyAnne wrote:
| Quote: |
The functions it's waiting for are not functions that it calls directly
(except that Application->ProcessMessages will call them if there are any to
be handled). It's waiting for functions that may be called from another
thread (functions within a child thread that can be called by the parent
thread), or waiting for event handler functions (OnError, OnConnect, etc.).
|
VCL's ProcessMessages will, in your words, "keep a thread alive without
CPU hogging".
..a
|
|
| Back to top |
|
 |
LesleyAnne Guest
|
Posted: Tue Aug 30, 2005 9:50 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
Ok, I've got something that sort of works, but not the way I'd like it to.
The thread has a socket connection, and one of the things it was waiting for
was communication on that socket. I rewrote it to use a blocking socket and
that takes care of that part, but at the expense of blocking out anything
else I wanted to happen. (For instance, now the main thread can't tell the
child thread to stop while the child thread is stuck waiting on that
blocking socket.) It worked much cleaner when I was using a non-blocking
socket and a
while (WaitingForResults) {
Application->ProcessMessages();
}
loop. With that setup I was able to wait for any of several different
things to happen, so the thread could do a lot more. Somehow the main
thread is able to handle this. How does Application->Run() manage to tell
the application's main thread to just sit there and react to any events that
occur? That's what I want the child thread to do as well - sit there and
react whenever an event occurs, but otherwise don't hog the CPU.
Lesley Anne
|
|
| Back to top |
|
 |
LesleyAnne Guest
|
Posted: Tue Aug 30, 2005 9:55 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
"Alex Bakaev [TeamB]" <zxtt (AT) att (DOT) net> wrote in message
| Quote: |
VCL's ProcessMessages will, in your words, "keep a thread alive without
CPU hogging".
|
How? I've tried:
while (WaitingForResults) {
Application->ProcessMessages();
}
and that sends the CPU usage to 100% as long as it's active.
|
|
| Back to top |
|
 |
Alex Bakaev [TeamB] Guest
|
Posted: Tue Aug 30, 2005 11:41 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
LesleyAnne wrote:
[snip]
The underlying APIs that ProcessMessages uses should take care of this.
If there are no messages, then there is nothing to process.
..a
|
|
| Back to top |
|
 |
Chris Uzdavinis (TeamB) Guest
|
Posted: Wed Aug 31, 2005 1:36 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
"Alex Bakaev [TeamB]" <zxtt (AT) att (DOT) net> writes:
| Quote: | LesleyAnne wrote:
[snip]
The underlying APIs that ProcessMessages uses should take care of
this. If there are no messages, then there is nothing to process.
|
I'm not sure it makes sense to call ProcessMessages from a thread in
the first place, since only the main thread should be updating VCL
objects.
--
Chris (TeamB);
|
|
| Back to top |
|
 |
Chris Uzdavinis (TeamB) Guest
|
Posted: Wed Aug 31, 2005 2:00 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
"LesleyAnne" <labaker (AT) snetnsa (DOT) com> writes:
| Quote: | The functions it's waiting for are not functions that it calls directly
(except that Application->ProcessMessages will call them if there are any to
be handled). It's waiting for functions that may be called from another
thread (functions within a child thread that can be called by the parent
thread), or waiting for event handler functions (OnError, OnConnect, etc.).
|
Ok. So if I understand correctly, you want to have a set of functions
of a TThread-derived object, and have the background thread sit idle
until any of these functions is called from the main thread. However,
when the function is actually called, you want the work to be
performed by the background thread and not the main thread.
If that's the case, it's known as the "Active Object" pattern.
But in a nutshell, you need a way for the main thread to indicate WHAT
function it wants to run in the background thread, and then wake up
the background thread to cause it to do the work. Right?
This is oftentimes done with a message queue. The main thread inserts
"commands" into the queue, and the background threads waits for the
condition that the queue is not empty. When something is inserted,
the queue accepts the command object, and the main thread immediately
returns. Then the queue signals the sleeping thread, which wakes up
and receives the command, and processes it.
To implement such a message queue, I'd suggest using a condition
variable as mentioned in an earlier post. The condition associated
with the variable is "queue is empty". The background thread simply
loops calling "get_next()" or whatever you call it, and that function
"blocks" until something becomes available.
The main thread calls a function on your class that creates a command
to represnt the work to be preformed, and inserts it into the queue
and signals the condition variable. That will cause the other thread
to wake up if it's blocked in the get_next() command.
If you use ACE, here's a simplified msg_queue that I use for that
purpose. If you don't use ACE, it may still give you an idea of how a
condition variable works. The background thread loops calling pop()
on the queue. (This would be done in Execute() in a TThread object).
Each time pop() returns, you have an object of whatever type your
queue holds. For lifetime management, I generally always allocate an
object to insert, and then delete it in the consumer thread when
finished with it. The information inside this object is inspected and
the background thread figures out what should be done. (I also use
the command pattern here, which puts the knowledge of what should be
don into the enqueued object itself, and then the worker thread simply
calls process() (a virtual function) on the dequeued object, and the
object "does its thing" depending on how it implements process().
The only requirement I have is that T inherits from
ACE_Intrusive_List_Node, so that it can be stuck into the ACE
intrusive list. For example, my abstract command class looks like this
#ifndef Abstract_Command_hpp_
#define Abstract_Command_hpp_
#include "ace/OS.h"
#include "ace/Intrusive_List_Node.h"
template <class T> class Msg_Queue;
class Deep_Book_Event_Handler;
class Abstract_Command
: public ACE_Intrusive_List_Node<Abstract_Command>
{
public:
Abstract_Command();
virtual ~Abstract_Command();
virtual void process(YourClass * caller) = 0;
};
#endif // Abstract_Command_hpp_
Then for each operation, I derive a different class from it, and its
process() function calls into the YourClass object. (I modified this
for the sake of this post, as I actually have it call into something
else, and you should modifiy it to suit your needs too.)
Here's the code for the queue.
// Header
#ifndef Msg_Queue_hpp_
#define Msg_Queue_hpp_
#include "ace/Condition_T.h"
#include "ace/Thread_Mutex.h"
#include "ace/Intrusive_List.h"
#include <stdlib.h>
// Simple messsage queue with a high-water-mark. If it
// fills to this level, new data is discarded until the
// queue is emptied. Then future writes are accepted.
//
// NOTE: hwm is measuring number of messages, not size.
//
template <class T>
class Msg_Queue
{
public:
Msg_Queue(size_t hwm = 1000000, size_t lwm = 10000);
~Msg_Queue();
void set_hwm(size_t hwm);
size_t hwm() const;
void set_lwm(size_t lwm);
size_t lwm() const;
void push(T * node);
T * pop();
private:
void operator=(Msg_Queue const &); // not implemented
Msg_Queue(Msg_Queue const &); // not implemented
void enqueue(T * node);
bool above_high_watermark() const;
typedef ACE_Guard<ACE_Thread_Mutex> Guard;
private:
ACE_Intrusive_List<T> messages_;
size_t msg_queue_length_; //main+WORKER thread
size_t high_water_mark_; //main thread
size_t low_water_mark_; //main+WORKER threads
size_t producers_blocked_;//main+WORKER threads
size_t consumers_blocked_;//main+WORKER threads
ACE_Thread_Mutex lock_;
ACE_Condition<ACE_Thread_Mutex> msg_exists_cv_;
ACE_Condition<ACE_Thread_Mutex> empty_enough_cv_;
};
#include "Msg_Queue.ipp"
#endif // Msg_Queue_hpp_
And the inline implementation file (notice I called it a .ipp file)
#ifndef Msg_Queue_ipp_
#define Msg_Queue_ipp_
#define TEMPLATE_ARGS template <class T>
#define MSG_QUEUE Msg_Queue<T>
// main thread
TEMPLATE_ARGS
MSG_QUEUE::
Msg_Queue(size_t hwm, size_t lwm)
: messages_()
, msg_queue_length_(0)
, high_water_mark_(hwm)
, low_water_mark_(lwm)
, producers_blocked_(0)
, consumers_blocked_(0)
, lock_()
, msg_exists_cv_(lock_)
, empty_enough_cv_(lock_)
{
}
// main thread
TEMPLATE_ARGS
MSG_QUEUE::
~Msg_Queue()
{
}
// main thread
TEMPLATE_ARGS
inline
void MSG_QUEUE::
set_hwm(size_t hwm)
{
high_water_mark_ = hwm;
}
// main thread
TEMPLATE_ARGS
inline
size_t MSG_QUEUE::
hwm() const
{
return high_water_mark_;
}
// main thread
TEMPLATE_ARGS
inline
void MSG_QUEUE::
set_lwm(size_t lwm)
{
Guard guard(lock_); // lwm is used MT
low_water_mark_ = lwm;
if (msg_queue_length_ <= lwm)
{
empty_enough_cv_.signal();
}
}
// main thread
TEMPLATE_ARGS
inline
size_t MSG_QUEUE::
lwm() const
{
Guard guard(lock_); // lwm is used MT
return low_water_mark_;
}
// main thread
TEMPLATE_ARGS
void MSG_QUEUE::
push(T * node)
{
Guard guard(lock_);
if (msg_queue_length_ > high_water_mark_)
{
++producers_blocked_;
while (msg_queue_length_ >= low_water_mark_)
{
empty_enough_cv_.wait();
}
--producers_blocked_;
}
messages_.push_front(node);
++msg_queue_length_;
if (consumers_blocked_ > 0)
{
msg_exists_cv_.signal();
}
}
// worker thread
TEMPLATE_ARGS
T * MSG_QUEUE::
pop()
{
Guard guard(lock_);
if (messages_.empty())
{
++consumers_blocked_;
do
{
msg_exists_cv_.wait();
} while (messages_.empty());
--consumers_blocked_;
}
--msg_queue_length_;
if ( (producers_blocked_ > 0)
&& (msg_queue_length_ <= low_water_mark_))
{
empty_enough_cv_.signal();
}
T * node = messages_.pop_back();
return node;
}
#undef MSG_QUEUE
#undef TEMPLATE_ARGS
#endif // Msg_Queue_ipp_
This class actually implements flow control on both sides. The
producer will block if the queue gets "too full" (fast producer, slow
consumer), and will block the consumer if the queue is empty.
Notice, when we dequeue, it checks to see if there is a producer
thread waiting to insert, and if it's "empty enough" it signals the
empty_enough condition variable. Conversely, when a worker thread is
blocked waiting for a message, it's sitting in the
msg_exists_cv_.wait() function, which blocks until the producer
signals that condition variable (which happens when it pushes a new
object into the queue.)
--
Chris (TeamB);
|
|
| Back to top |
|
 |
LesleyAnne Guest
|
Posted: Wed Aug 31, 2005 6:55 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
"Chris Uzdavinis (TeamB)" <chris (AT) uzdavinis (DOT) com> wrote
| Quote: |
If that's the case, it's known as the "Active Object" pattern.
But in a nutshell, you need a way for the main thread to indicate WHAT
function it wants to run in the background thread, and then wake up
the background thread to cause it to do the work. Right?
This is oftentimes done with a message queue. The main thread inserts
"commands" into the queue, and the background threads waits for the
condition that the queue is not empty. When something is inserted,
the queue accepts the command object, and the main thread immediately
returns. Then the queue signals the sleeping thread, which wakes up
and receives the command, and processes it.
To implement such a message queue, I'd suggest using a condition
variable as mentioned in an earlier post. The condition associated
with the variable is "queue is empty". The background thread simply
loops calling "get_next()" or whatever you call it, and that function
"blocks" until something becomes available.
The main thread calls a function on your class that creates a command
to represnt the work to be preformed, and inserts it into the queue
and signals the condition variable. That will cause the other thread
to wake up if it's blocked in the get_next() command.
If you use ACE, here's a simplified msg_queue that I use for that
purpose. If you don't use ACE, it may still give you an idea of how a
condition variable works.
|
Well, I've never heard of ACE, but I think I see roughly what's happening
there. Thanks. With a bit of effort I can probably come up with something
equivalent and make it work for thread functions I call from within my own
code (in the main thread).
I don't see how to work event handlers into it, though. The thread contains
components (in this case a TClientSocket, though I've had similar things
come up with other components involved) and I need any event triggers from
its components to also wake up the thread so I can run the associated event
handler. Would a component in a suspended or blocked thread even function
enough to trigger events? And how would I get those triggers into my own
queue? Or how would I access the queue where they are? (I know component
event triggers are really messages that go through a queue somewhere, but
since I don't access that queue directly, I don't know how to make the
existence of a message in it wake up the thread.)
I need something in my Execute function that acts basically like:
while (!Terminated) {
block until there's something in the queue
ProcessMessages();
}
but the queue it's checking has to contain messages both from components in
the thread and also from other threads calling its functions.
|
|
| Back to top |
|
 |
Chris Uzdavinis (TeamB) Guest
|
Posted: Wed Aug 31, 2005 6:58 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
"LesleyAnne" <labaker (AT) snetnsa (DOT) com> writes:
| Quote: | I don't see how to work event handlers into it, though.
|
The event handlers run in the main thread. They would call into your
thread object, which allocates a command object and inserts it into
the message queue. The thread wakes up and processes the message.
Thus, the event handler doesn't actually do the work. It simply gets
the process started.
The message queue can be "hidden" inside your thread object. So the
event handler can simply call a member function of your thread object
that actually does the allocation/insertion of the command object, all
behind the scenes.
--
Chris (TeamB);
|
|
| Back to top |
|
 |
Alex Bakaev [TeamB] Guest
|
Posted: Wed Aug 31, 2005 6:59 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
Chris Uzdavinis (TeamB) wrote:
| Quote: |
I'm not sure it makes sense to call ProcessMessages from a thread in
the first place, since only the main thread should be updating VCL
objects.
|
Yes, agreed. If a separate worker thread is required, then waiting on
the condition variable is the correct pattern to use.
..a
|
|
| Back to top |
|
 |
Alex Bakaev [TeamB] Guest
|
Posted: Wed Aug 31, 2005 7:04 pm Post subject: Re: how to keep a thread alive without a CPU-hogging infinit |
|
|
Chris Uzdavinis (TeamB) wrote:
| Quote: | queue holds. For lifetime management, I generally always allocate an
object to insert, and then delete it in the consumer thread when
finished with it. The information inside this object is inspected and
|
To simplify this even further, I'd suggest boost::shared_ptr for the
command objects.
..a
|
|
| 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
|
|