如何使pthread_cond_timedwait()对系统时钟操作具有鲁棒性?

Mec*_*cki 15 time posix pthreads clock

请考虑以下源代码,它完全符合POSIX:

#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>

int main (int argc, char ** argv) {
    pthread_cond_t c;
    pthread_mutex_t m;
    char printTime[UCHAR_MAX];

    pthread_mutex_init(&m, NULL);
    pthread_cond_init(&c, NULL);

    for (;;) {
        struct tm * tm;
        struct timeval tv;
        struct timespec ts;

        gettimeofday(&tv, NULL);

        printf("sleep (%ld)\n", (long)tv.tv_sec);
        sleep(3);

        tm = gmtime(&tv.tv_sec);
        strftime(printTime, UCHAR_MAX, "%Y-%m-%d %H:%M:%S", tm);
        printf("%s (%ld)\n", printTime, (long)tv.tv_sec);

        ts.tv_sec = tv.tv_sec + 5;
        ts.tv_nsec = tv.tv_usec * 1000;

        pthread_mutex_lock(&m);
        pthread_cond_timedwait(&c, &m, &ts);
        pthread_mutex_unlock(&m);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

每5秒打印一次当前系统日期,但是在获取当前系统时间(gettimeofday)和条件wait(pthread_cond_timedwait)之间它会休眠3秒.

在打印"sleep(...)"之后,尝试将系统时钟设置为过去两天.怎么了?好吧,而不是像往常一样pthread_cond_timedwait等待2秒钟,现在等待两天 2秒.

我该如何解决这个问题?
如何编写POSIX兼容代​​码,在用户操作系统时钟时不会中断?

请记住,即使没有用户交互,系统时钟也可能会发生变化(例如,NTP客户端可能每天自动更新一次时钟).将时钟设置为未来是没有问题的,它只会导致睡眠提前唤醒,这通常没有问题,您可以轻松地"检测"并相应地处理,但将时钟设置为过去(例如,因为它是在未来运行,NTP检测到并修复它可能会导致一个大问题.

PS:我的系统上
既不存在pthread_condattr_setclock()也不CLOCK_MONOTONIC存在.这些对于POSIX 2008规范("Base"的一部分)是必需的,但是大多数系统仍然只遵循当前的POSIX 2004规范,而在POSIX 2004规范中这两个是可选的(高级实时扩展).

pax*_*blo 4

有趣的是,我以前没有遇到过这种行为,但是话又说回来,我没有那么多乱搞系统时间的习惯:-)

假设您出于正当理由这样做,一种可能的(尽管很笨拙)解决方案是使用另一个线程,其唯一目的是定期启动条件变量以唤醒任何受影响的线程。

换句话说,类似:

while (1) {
    sleep (10);
    pthread_cond_signal (&condVar);
}
Run Code Online (Sandbox Code Playgroud)

等待条件变量被踢出的代码无论如何都应该检查其谓词(以处理虚假唤醒),因此这不应该对功能产生任何真正的有害影响。

这对性能有轻微影响,但每十秒一次应该不是什么太大的问题。它的真正目的只是为了处理(无论出于何种原因)您的定时等待将等待很长时间的情况。


另一种可能性是重新设计您的应用程序,以便您根本不需要定时等待。

在出于某种原因需要唤醒线程的情况下,它总是由另一个线程完全能够启动条件变量来唤醒一个线程(或通过广播来唤醒许多线程)。

这与我上面提到的踢线非常相似,但更多的是作为架构的一个组成部分而不是螺栓固定。