如何阻止阻塞的win32线程?

时间:2011-10-27 19:34:32

标签: c++ multithreading winapi concurrency

我创建了一个自定义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))?此外,还有比这更好的方法吗?

2 个答案:

答案 0 :(得分:6)

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

  

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

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

所以,总结一下:

  1. 您的策略很好,TerminationTask::Run的实施是正确的。
  2. 您可以在_endthreadex(0)结束时删除对ThreadPool::Loop的无害呼叫。

答案 1 :(得分:0)

在队列中放置一个temination任务是正确的。不过,我会尝试不同的方法来处理它:

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