【问题标题】:How to stop a win32 thread that is blocking?如何停止阻塞的win32线程?
【发布时间】:2011-10-27 19:34:32
【问题描述】:

我创建了一个自定义ThreadPool,它使用_beginthreadex() 启动了许多win32 线程。线程正在运行一个简单的循环,试图从阻塞队列中取出任务,但有时我需要停止线程,如果它们在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;
        }
    }
}

我的想法是将相同数量的特殊任务(我们称之为“终止任务”)排入队列,因为池中有线程,每个“终止任务”将调用_endthreadex(0) 以退出线程。如果阻塞队列中还有其他任务,那么我不会真正关心,因为一旦我将任务出列,我将运行它,我将检查 _running 标志以确定线程是否需要将更多任务出列.

void TerminationTask::Run()
{
    _endthreadex(0);
}

我对这种方法有几个担忧;主要是,如果我处理了一个非终止任务并且_running 标志设置为false,那么我的线程在退出循环时将不会调用_endthreadex(0)。我想知道是否可以像这样在循环结束时调用_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);
}

这会导致与我的TerminationTask 发生冲突,还是线程会在执行TerminationTask::Run() 后直接退出循环(即它不会调用_endthreadex(0) 两次)?此外,还有比这更好的方法吗?

【问题讨论】:

    标签: c++ multithreading winapi concurrency


    【解决方案1】:

    在线程方法结束时调用_endthreadex(0) 就可以了。它也是可选的。如果你只是正常离开线程方法,那么会为你调用_endthreadex(0)

    您可以显式调用 _endthread 或 _endthreadex 来终止线程;但是,当线程从作为参数传递给 _beginthread 或 _beginthreadex 的例程返回时,会自动调用 _endthread 或 _endthreadex。ref

    发送终止任务是让被阻塞的线程池线程解除阻塞并退出的正确方法。

    所以,总结一下:

    1. 你的策略不错,TerminationTask::Run的实现是正确的。
    2. 您可以在ThreadPool::Loop 末尾删除对_endthreadex(0) 的无害调用。

    【讨论】:

    • 啊,明白了...我将删除不需要的_endthreadex(0)!感谢您的确认!
    • 顺便说一句,您需要while (_running) 循环吗?每当我这样做时,我都会写while (true),然后发送终止任务以结束诉讼程序。既然你有一个终止机制,那么在_running 标志中还有一个似乎是虚假的。事实上,我怀疑以这种方式编写代码会不必要地复杂,并且可能会出现竞争条件。
    • 我需要_running 标志,以防我在队列中有大量任务并且我想在处理所有任务之前终止。终止任务仅在我没有队列中的任务并且我想退出阻塞状态时才有用。换句话说:我可以有一个ThreadPool::Terminate(它允许通过简单地将TerminateTask添加到阻塞队列的末尾并禁用入队来处理所有任务)我可以有一个ThreadPool::TerminateNow方法,它只是无论队列中有什么,都停止处理任务。
    • +1 但另外几个选项可能是 1) 专门设置一个“死亡事件”来唤醒在 WaitForMultipleObjects 中阻塞的线程或 2) 通过 QueueUserAPC 发送一个“死亡作业”跨度>
    • @JohnDibling 你能解释一下怎么做吗?您的评论让我想到了Runnable 任务有一些阻塞代码的情况......我无法控制它,那么中断Runnable 是否也是一个好政策?也许我应该为Runnable 实施某种策略,以便它有一个“中断”方法,使它脱离阻塞状态(这将是实现接口的人的责任来保证这一点)。跨度>
    【解决方案2】:

    将终止任务放入队列是正确的。不过,我会尝试不同的方法来处理它:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-02
      • 2013-07-05
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多