我有一个完全线程安全的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;
}
我的问题是,有时候,任务列表中没有新任务,因此处理线程进入无限循环(while(!WorkIsOver)
)并且CPU负载增加。不知何故,我必须让线程等待,直到新任务存储在列表中。我考虑暂停和恢复,但后来我需要关于哪些线程挂起或运行的额外信息,这给编码带来了更大的复杂性。
有什么想法吗?
PS。我使用的是winapi,而不是Boost或TBB用于线程化。因为有时我必须终止处理时间过长的线程,并立即创建新的线程。这对我来说至关重要。请不要建议这两个中的任何一个。
由于
答案 0 :(得分:7)
假设您正在使用DevStudio进行开发,您可以使用[IO完成端口]获得所需的控件。可怕的名字,对于一个简单的工具。
多数民众赞成。 3个API调用,您已经实现了基于内核实现的队列对象的线程池机制。每次调用PostQueuedCompletionStatus都会自动反序列化到GetQueuedCompletionStatus上阻塞的线程池线程。工作线程池由您创建和维护 - 因此您可以在任何花费太长时间的工作线程上调用TerminateThread。更好 - 根据它的设置方式,内核只会唤醒所需的线程,以确保每个CPU内核在~100%负载下运行。
NB。 TerminateThread实际上不是一个合适的API。除非你真的知道你在做什么,否则线程会泄漏它们的堆栈,线程上的代码分配的内存都不会被释放,依此类推。 TerminateThread实际上只在进程关闭期间有用。网上有一些文章详细说明了如何释放每次调用TerminateThread时泄露的已知操作系统资源 - 如果你坚持这种方法,你真的需要找到并阅读它们,如果你还没有。
答案 1 :(得分:2)
::ReleaseSemaphore
以增加与信号量相关的计数::WaitForSingleObject()
- 您可以等待超时,以便您有机会知道您的线程应该退出。否则,只要有一个或多个要处理的项目,你的线程就会被唤醒,并且还有一个很好的副作用就是减少你的信号量。答案 2 :(得分:2)
如果您还没有阅读过,那么您应该吞噬Herb Sutter的Effective Concurrency系列,该系列涵盖了这个主题以及更多内容。
答案 3 :(得分:1)
使用条件变量来实现生产者/消费者队列 - 示例代码here。
如果您需要支持早期版本的Windows,可以在Boost中使用条件变量。或者,您可以通过从Boost标头中复制特定于Windows的代码来构建自己的代码,它们使用相同的Win32 API,就像您构建自己的代码一样。
答案 4 :(得分:0)
为什么不使用现有的线程池?让Windows管理所有这些。
答案 5 :(得分:0)
答案 6 :(得分:0)
如果TaskList有某种wait_until_not_empty方法,那么就使用它。如果没有,那么一个Sleep(1000)(或其他一些值)可能就是这样做的。正确的解决方案是在TaskList周围创建一个包装器,它使用自动重置事件句柄来指示列表是否为空。您需要重新发明pop / push的当前方法,新任务列表是新类的成员:
WaitableTaskList::WaitableTaskList()
{
// task list is empty upon creation
non_empty_event = CreateEvent(NULL, FALSE, FALSE, NULL);
}
Task* WaitableTaskList::wait_and_pop_front(DWORD timeout)
{
WaitForSingleObject(non_empty_event, timeout);
// .. handle error, return NULL on timeout
Task* result = task_list.pop_front();
if (!task_list.empty())
SetEvent(non_empty_event);
return result;
}
void WaitableTaskList::push_back(Task* item)
{
task_list.push_back(item);
SetEvent(non_empty_event);
}
您必须仅通过此wait_and_pop_front()
等方法在任务列表中弹出项目。