我有一个完全线程安全的FIFO结构(TaskList)来存储任务类,多个线程,其中一些创建和存储任务,其他线程处理任务. TaskList类有一个pop_front()方法,如果至少有一个,则返回第一个任务.否则返回NULL.
这是一个处理函数的例子:
TaskList tlist;
unsigned _stdcall ThreadFunction(void * qwe)
{
Task * task;
while(!WorkIsOver) // a global bool to end all threads.
{
while(task = tlist.pop_front())
{
// process Task
}
}
return 0;
}
我的问题是,有时候,任务列表中没有新任务,因此处理线程进入无限循环(而(!WorkIsOver))并且CPU负载增加.不知何故,我必须让线程等待,直到新任务存储在列表中.我考虑暂停和恢复,但后来我需要关于哪些线程挂起或运行的额外信息,这给编码带来了更大的复杂性.
有任何想法吗?
PS.我使用的是winapi,而不是Boost或TBB用于线程化.因为有时我必须终止处理时间过长的线程,并立即创建新的线程.这对我来说至关重要.请不要建议这两个中的任何一个.
谢谢
解决方法:
假设您正在使用DevStudio进行开发,可以使用[IO完成端口]获得所需的控件.可怕的名字,一个简单的工具.
>首先,创建一个IOCompletion端口:CreateIOCompletionPort
>使用_beginthreadex / CreateThread创建工作线程池
>在每个工作线程中,实现一个调用GetQueuedCompletionStatus的循环 – 返回的lpCompletionKey将指向要处理的工作项.
>现在,每当您获得要处理的工作项时:从任何线程调用PostQueuedCompletionStatus – 将指向您工作项的指针作为完成键参数传递.
而已. 3个API调用,您已经实现了基于内核实现的队列对象的线程池机制.每次调用PostQueuedCompletionStatus都会自动反序列化到GetQueuedCompletionStatus上阻塞的线程池线程.工作线程池由您创建和维护 – 因此您可以在任何花费太长时间的工作线程上调用TerminateThread.更好 – 根据它的设置方式,内核只会唤醒所需的线程,以确保每个CPU内核在~100%负载下运行.
NB. TerminateThread实际上不是一个合适的API.除非你真的知道你在做什么,否则线程会泄漏它们的堆栈,线程上的代码分配的内存都不会被释放,依此类推. TerminateThread实际上只在进程关闭期间有用.网上有一些文章详细说明了如何释放每次调用TerminateThread时泄露的已知操作系统资源 – 如果你坚持这种方法,你真的需要找到并阅读它们,如果你还没有.