简体   繁体   English

如何停止正在阻塞的Win32线程?

[英]How to stop a win32 thread that is blocking?

I've created a custom ThreadPool which starts a number of win32 threads with _beginthreadex() . 我创建了一个自定义ThreadPool ,它使用_beginthreadex()启动了多个win32线程。 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. 线程正在运行一个简单的循环,试图从阻塞队列中使任务出队,但是有时我需要停止线程,如果它们在Dequeue被阻塞,那么我不知道如何使线程脱离阻塞状态。

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. 我的想法是排队与池中有线程相同数量的特殊任务(我们称它们为“终止任务”),每个“终止任务”将调用_endthreadex(0)以便退出线程。 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. 如果阻塞队列中还有其他任务,那么我就不会在乎,因为一旦我将一个任务出队,我将立即运行它,并检查_running标志以确定线程是否需要将其他任务出队。

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. 主要是,如果我处理了一个非终止任务并且_running标志设置为false ,那么我的线程退出循环时将不会调用_endthreadex(0) I was wondering if I could call _endthreadex(0) at the end of the loop like this: 我想知道是否可以在循环结束时像这样调用_endthreadex(0)

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)? 这会导致与我的TerminationTask发生冲突,还是线程在执行TerminationTask::Run()之后直接退出循环(即它将不会两次调用_endthreadex(0) )? Furthermore, is there a better approach than this? 此外,还有没有比这更好的方法了?

Calling _endthreadex(0) at the end of the thread method is fine. 在线程方法末尾调用_endthreadex(0)很好。 It is also optional. 它也是可选的。 If you just leave the thread method normally, then _endthreadex(0) is called for you. 如果您只是正常退出线程方法,则会为您调用_endthreadex(0)

You can call _endthread or _endthreadex explicitly to terminate a thread; 您可以显式调用_endthread或_endthreadex终止线程。 however, _endthread or _endthreadex is called automatically when the thread returns from the routine passed as a parameter to _beginthread or _beginthreadex. 但是,当线程从作为参数传递给_beginthread或_beginthreadex的例程返回时,会自动调用_endthread或_endthreadex。 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. 您的策略是好的,并且TerminationTask::Run的实现是正确的。
  2. You can remove the harmless call to _endthreadex(0) at the end of ThreadPool::Loop . 您可以在ThreadPool::Loop的末尾删除对_endthreadex(0)的无害调用。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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