pthread_cond_timedwait 负载过大 CPU 时超时
pthread_cond_timedwait timing out late when large load put on CPU
在为对象编写单元测试时,我注意到 pthread_cond_timedwait 在 CPU 上加载大负载时不会很快超时。如果这些负载没有放在 CPU 上,一切正常。然而,当系统加载时,我发现无论我将超时设置为多长时间,真正的延迟都会减少大约 50-100 毫秒。
例如,这是程序单个间隔的打印输出,其中最后时间和当前时间是使用函数 GetTimeInMs 找到的。
// Printout, values are in ms
Last: 89799240
Current: 89799440
Period Length: 200
Expected Period: 100
据我所知,这个问题通常是由于使用相对时间而不是绝对时间引起的,但据我所知,我们正确使用了绝对时间。如果你们这些很棒的人可以帮助我弄清楚这里做错了什么,我将不胜感激。
此处显示了使用timedwait 的函数。请注意,基于我所做的定时调试,我知道生成的额外时间是通过 timedwait 调用完成的,因此我没有包含其他不必要的代码。
bool func(unsigned long long int time = 100) // ms
{
struct timespec ts;
pthread_mutex_lock(&m_Mutex);
if (0 == m_CurrentCount)
{
// Current time + delay in ns
unsigned long long int absnanotime = (GetTimeInMs()+time)*1000000;
struct timespec ts;
ts.tv_nsec = absnanotime % 1000000000ULL;
ts.tv_sec = absnanotime / 1000000000ULL;
do
{
if (0 != pthread_cond_timedwait(&m_Condition, &m_Mutex, &ts))
{
// In the case I am testing, I hope to get here via timeout in 100 ms
pthread_mutex_unlock(&m_Mutex);
return false;
}
}
while (!m_CurrentCount);
}
pthread_mutex_unlock(&m_Mutex);
return true;
}
unsigned long long int GetTimeInMs()
{
unsigned long long int time;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
time = ts.tv_nsec + ts.tv_sec * 1000000000ULL;
time = time / 1000000ULL; // Converts to ms
return time;
}
用于初始化 func 中使用的 class 个变量的代码。
void init()
{
pthread_mutex_init(&m_Mutex, NULL);
pthread_condattr_init(&m_Attr);
pthread_condattr_setclock(&m_Attr, CLOCK_MONOTONIC);
pthread_cond_init(&m_Condition, &m_Attr);
}
模拟CPU负载的CPU吞噬线程是运行下面的while循环。
void cpuEatingThread()
{
while (false == m_ShutdownRequested);
{
// m_UselessFoo is of type float*
m_UselessFoo = new float(1.23423525);
delete m_UselessFoo;
}
}
很可能,当等待超时时,线程在没有任何优先级提升或任何其他类似 action/s 的情况下就绪。如果盒子装满了,那么就绪线程可能不会立即变成运行。
将临时优先级提升应用于准备好接收信号的线程是很常见的 - 这往往会在信号在超时之前到达的 'usual' 情况下提高整体性能。超时通常更像是一个 'unusual' 事件,通常表示某种不会重复的失败,因此在超时时准备就绪的线程可以等待轮到他们:)
对于一般的定时等待,要求是他们至少等待他们的参数。如果您想要精确的时间,这不是正确的工具;您需要 保证 特定时间的东西,而且通常只在实时操作系统 (RTOS) 中可用。
在为对象编写单元测试时,我注意到 pthread_cond_timedwait 在 CPU 上加载大负载时不会很快超时。如果这些负载没有放在 CPU 上,一切正常。然而,当系统加载时,我发现无论我将超时设置为多长时间,真正的延迟都会减少大约 50-100 毫秒。
例如,这是程序单个间隔的打印输出,其中最后时间和当前时间是使用函数 GetTimeInMs 找到的。
// Printout, values are in ms
Last: 89799240
Current: 89799440
Period Length: 200
Expected Period: 100
据我所知,这个问题通常是由于使用相对时间而不是绝对时间引起的,但据我所知,我们正确使用了绝对时间。如果你们这些很棒的人可以帮助我弄清楚这里做错了什么,我将不胜感激。
此处显示了使用timedwait 的函数。请注意,基于我所做的定时调试,我知道生成的额外时间是通过 timedwait 调用完成的,因此我没有包含其他不必要的代码。
bool func(unsigned long long int time = 100) // ms
{
struct timespec ts;
pthread_mutex_lock(&m_Mutex);
if (0 == m_CurrentCount)
{
// Current time + delay in ns
unsigned long long int absnanotime = (GetTimeInMs()+time)*1000000;
struct timespec ts;
ts.tv_nsec = absnanotime % 1000000000ULL;
ts.tv_sec = absnanotime / 1000000000ULL;
do
{
if (0 != pthread_cond_timedwait(&m_Condition, &m_Mutex, &ts))
{
// In the case I am testing, I hope to get here via timeout in 100 ms
pthread_mutex_unlock(&m_Mutex);
return false;
}
}
while (!m_CurrentCount);
}
pthread_mutex_unlock(&m_Mutex);
return true;
}
unsigned long long int GetTimeInMs()
{
unsigned long long int time;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
time = ts.tv_nsec + ts.tv_sec * 1000000000ULL;
time = time / 1000000ULL; // Converts to ms
return time;
}
用于初始化 func 中使用的 class 个变量的代码。
void init()
{
pthread_mutex_init(&m_Mutex, NULL);
pthread_condattr_init(&m_Attr);
pthread_condattr_setclock(&m_Attr, CLOCK_MONOTONIC);
pthread_cond_init(&m_Condition, &m_Attr);
}
模拟CPU负载的CPU吞噬线程是运行下面的while循环。
void cpuEatingThread()
{
while (false == m_ShutdownRequested);
{
// m_UselessFoo is of type float*
m_UselessFoo = new float(1.23423525);
delete m_UselessFoo;
}
}
很可能,当等待超时时,线程在没有任何优先级提升或任何其他类似 action/s 的情况下就绪。如果盒子装满了,那么就绪线程可能不会立即变成运行。
将临时优先级提升应用于准备好接收信号的线程是很常见的 - 这往往会在信号在超时之前到达的 'usual' 情况下提高整体性能。超时通常更像是一个 'unusual' 事件,通常表示某种不会重复的失败,因此在超时时准备就绪的线程可以等待轮到他们:)
对于一般的定时等待,要求是他们至少等待他们的参数。如果您想要精确的时间,这不是正确的工具;您需要 保证 特定时间的东西,而且通常只在实时操作系统 (RTOS) 中可用。