简体   繁体   中英

How to stop a win32 thread that is blocking?

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() (ie it won't call _endthreadex(0) twice)? Furthermore, is there a better approach than this?

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:

  1. Your strategy is good and the implementation of TerminationTask::Run is correct.
  2. You can remove the harmless call to _endthreadex(0) at the end of ThreadPool::Loop .

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
        }
    }
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM