【发布时间】:2013-07-29 07:45:14
【问题描述】:
我在多线程应用程序中有一个罕见的 heisenbug,其中主线程,只有这个线程,什么都不做。因为它是一只海森虫,所以很难理解为什么会发生这种情况。
主线程基本上只是循环。在循环中,它检查几个并发优先级队列,其中包含按时间排序的任务以执行。它弹出一个任务,看看是否该执行它。如果时间到了,它只会将它安排到 TBB 的任务调度程序中(使用一个根任务,它是所有其他任务的父任务)。如果不是时间,任务被再次推送到优先级队列中。 这是一个周期。在循环结束时,主线程会休眠很短的时间,我希望在实践中会更长,但这并不是一个真正的问题,我只是不希望它在不必要的时候使用太多资源。
字面意思:
static const auto TIME_SCHEDULED_TASKS_SPAWN_FREQUENCY = microseconds(250);
while( !m_task_scheduler.is_exiting() ) // check if the application should exit
{
m_clock_scheduler.spawn_realtime_tasks(); // here we spawn tasks if it's time
this_thread::sleep_for( TIME_SCHEDULED_TASKS_SPAWN_FREQUENCY );
}
m_clock_scheduler.clear_tasks();
m_root_task.wait_for_all();
我有一个特殊任务,每秒只记录一条“TICK”消息。它会自动重新安排,直到程序结束。但是,当出现 heisenbug 时,我可以看到“TICK”消失了,并且应用程序除了在非主线程中发生的工作之外没有做任何其他事情。所以似乎只触及了主线程。
问题可能来自不同的地方,可能在调度逻辑中,但它也是唯一有睡眠调用的线程。睡眠是一个 boost::this_thread::sleep_for()。
我的问题是:Windows(7 64 位)是否有可能认为主线程经常处于休眠状态,并决定它应该休眠的时间比要求的时间长,或者肯定会结束?
我认为这是不可能的,但我想确定一下。到目前为止,我在在线文档中没有找到任何精确性。
更新:
我有一个朋友可以系统地重现该错误(在 Windows Vista、Core 2 Duo 上)。我给他发送了一个没有睡眠的版本,另一个使用 condition_variable 重新实现了主循环,这样每次将任务推入队列时,condition_variable 都会唤醒主线程(但仍然有最短的生成时间)。
没有睡眠的版本可以工作(但速度较慢) - 所以即使我不知道真正的来源,问题似乎也是相关的。
使用 condition_variable 的版本有效 - 这表明它是睡眠调用不能正常工作?
所以,显然我修复了这个错误,但我仍然不知道为什么特定的睡眠调用有时会阻塞。
更新:
这实际上是由 Boost 代码触发的错误。我找到了这个错误并报告了它,它已经被修复了。我没有检查以前的版本,但它已在 Boost 1.55 中修复
【问题讨论】:
-
你不能用调试器闯入进程,看看线程在哪里等待吗?我会说它正在等待某个其他线程未能释放的锁。
-
是否所有线程都具有相同的优先级,还是因为其他具有更高优先级的线程占用了处理时间,所以它只是像预期的那样休眠了更长的时间?
-
@avakar 不幸的是,由于它是一种罕见的 heisenbug,我无法轻松复制它,所以我现在无法做到这一点。另外,我在发布模式下比调试更经常看到这个错误,但它也出现在调试中,所以......
-
@ogni42 我从不触及线程优先级,因为这就像在你的程序中调用邪恶并让混乱的军队在其中被释放。话虽如此,还有另外两个线程正在运行,一个我使用 boost::thread 启动自己,另一个由我不知道它到底做什么的库 (RakNet) 启动。也许那里有问题,但它不应该影响主线程?
-
另外,您可以找到一种方法来摆脱持续轮询。 Sleep() 循环不能替代有效的线程间信号:)
标签: c++ multithreading boost tbb