Reputation: 40345
I've created a custom ThreadPool
which starts a number of win32 threads with _beginthreadex()
. The threads are running a simple loop that attempts to dequeue tasks from a blocking queue, but sometimes I need to stop the threads and if they're blocked on Dequeue
then I don't know how to get the threads out of that blocking state.
void ThreadPool::Loop()
{
while(_running)
{
try
{
// Attempts to dequeue a task and run it
_taskQueue.Dequeue()->Run();
}
catch(BlockingQueueTerminate&)
{
// Eat the exception and check the running flag
continue;
}
}
}
My idea was to enqueue the same number of special tasks (let's call them "termination tasks") as there are threads in the pool and each "termination task" will call _endthreadex(0)
in order to exit the thread. If there are other tasks in the blocking queue, then I won't really care because as soon as I dequeue a task, I will run it and I will check the _running
flag to determine if the thread needs to dequeue any more tasks.
void TerminationTask::Run()
{
_endthreadex(0);
}
I have several concerns about this approach; mainly, if I processed a non-terminating task and the _running
flag is set to false
, then my thread will not call _endthreadex(0)
when it exits the loop. I was wondering if I could call _endthreadex(0)
at the end of the loop like this:
void ThreadPool::Loop()
{
while(_running)
{
try
{
// Attempts to dequeue a task and run it
_taskQueue.Dequeue()->Run();
}
catch(BlockingQueueTerminate&)
{
// Eat the exception and check the running flag
continue;
}
}
_endthreadex(0);
}
Will this cause a conflict with my TerminationTask
or will the thread exit the loop directly after executing TerminationTask::Run()
(i.e. it won't call _endthreadex(0)
twice)? Furthermore, is there a better approach than this?
Upvotes: 5
Views: 2196
Reputation: 595295
Putting a temination task in the Queue is correct. I would try a different approach to handling it, though:
class TerminateThreadNow {};
void TerminationTask::Run()
{
throw TerminateThreadNow();
}
void ThreadPool::Loop()
{
while(_running)
{
try
{
// Attempts to dequeue a task and run it
_taskQueue.Dequeue()->Run();
}
catch(TerminateThreadNow&)
{
_running = false;
}
catch(BlockingQueueTerminate&)
{
// Eat the exception and check the running flag
}
}
}
Upvotes: 0
Reputation: 612784
Calling _endthreadex(0)
at the end of the thread method is fine. It is also optional. If you just leave the thread method normally, then _endthreadex(0)
is called for you.
You can call _endthread or _endthreadex explicitly to terminate a thread; however, _endthread or _endthreadex is called automatically when the thread returns from the routine passed as a parameter to _beginthread or _beginthreadex.ref
Sending a termination task is the correct way to get a blocked thread pool thread to unblock and quit.
So, to summarise:
TerminationTask::Run
is correct._endthreadex(0)
at the end of ThreadPool::Loop
.Upvotes: 6