我创建了一个自定义的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)
两次)?此外,还有比这更好的方法吗?
在线程方法结束时调用_endthreadex(0)
是可以的。它也是可选的。如果您只是正常离开线程方法,那么_endthreadex(0)
将为您调用。
可以显式调用_endthread或_endthreadex来终止线程;然而,当线程从作为参数传递给_beginthread或_beginthreadx的例程返回时,会自动调用_endthread或_endthreadx。ref
发送终止任务是让阻塞的线程池线程解除阻塞并退出的正确方法。
总结一下:
- 你们的策略是好的,
TerminationTask::Run
的实施是正确的。 - 可以在
ThreadPool::Loop
的末尾删除对_endthreadex(0)
的无害呼叫。
将终止任务放入队列是正确的。不过,我会尝试一种不同的方法来处理它:
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
}
}
}