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

如何使pthread_cond_timedwait()对系统时钟操作具有鲁棒性?,time,pthreads,posix,clock,Time,Pthreads,Posix,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_mu

考虑以下完全符合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;
}
#包括
#包括
#包括
#包括
#包括
#包括
int main(int argc,字符**argv){
pthread_cond_t c;
pthread_mutex_t m;
字符打印时间[UCHAR_MAX];
pthread_mutex_init(&m,NULL);
pthread_cond_init(&c,NULL);
对于(;;){
struct tm*tm;
结构时间值电视;
结构timespects;
gettimeofday(&tv,NULL);
printf(“睡眠(%ld)\n),(长)tv.tv\u秒);
睡眠(3);
tm=gmtime(&tv.tv_秒);
strftime(打印时间,UCHAR_MAX,“%Y-%m-%d%H:%m:%S”,tm);
printf(“%s(%ld)\n”,打印时间,(长)tv.tv\u秒);
ts.tv_sec=tv.tv_sec+5;
ts.tv\u nsec=tv.tv\u usec*1000;
pthread_mutex_lock(&m);
pthread_cond_timedwait(&c,&m,&ts);
pthread_mutex_unlock(&m);
}
返回0;
}
每5秒打印一次当前系统日期,但是,在获取当前系统时间(
gettimeofday
)和条件等待(
pthread\u cond\u timedwait
)之间,它会休眠3秒

在打印“sleep(…)”之后,尝试将系统时钟设置为过去两天。会发生什么?好的,不再像通常那样在条件下再等待2秒,
pthread\u cond\u timedwait
现在等待两天和2秒

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

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

附言:

我的系统上既不存在
pthread\u condattr\u setclock()
也不存在
CLOCK\u MONOTONIC
。这些是POSIX 2008规范(Base的一部分)的强制性要求,但到今天为止,大多数系统仍然只遵循POSIX 2004规范,而在POSIX 2004规范中,这两个都是可选的(高级实时扩展)。

有趣的是,我以前没有遇到过这种行为,但是,我不习惯在我的系统中浪费那么多时间:-)

假设您这样做是出于一个合理的原因,一个可能的(尽管很麻烦)解决方案是使用另一个线程,其唯一目的是周期性地触发条件变量以唤醒任何受影响的线程

换句话说,类似于:

while (1) {
    sleep (10);
    pthread_cond_signal (&condVar);
}
等待条件变量被踢出的代码无论如何都应该检查其谓词(以防止虚假唤醒),因此这不应该对功能产生任何实际的有害影响

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


另一种可能是重新设计应用程序,这样就根本不需要定时等待

在由于某种原因需要唤醒线程的情况下,它总是被另一个线程唤醒,而另一个线程完全能够踢出一个条件变量来唤醒一个线程(或者广播来唤醒它们中的很多线程)


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

您可以针对这个问题保护代码。一个简单的方法是使用一个线程,其唯一目的是监视系统时钟。您保留条件变量的全局链接列表,如果时钟观察线程看到系统时钟跳转,它将广播列表中的每个条件变量。然后,您只需将
pthread_cond_init
pthread_cond_destroy
包装为向全局链表添加/删除条件变量的代码。用互斥锁保护链表。

我很恼火,这个函数需要一个
timespec
struct并使用绝对时间。我想我将使用
pipe()
/
select()
方法来绕过它。