如何使pthread_cond_timedwait()对系统时钟操作具有鲁棒性? [英] How to make pthread_cond_timedwait() robust against system clock manipulations?

查看:139
本文介绍了如何使pthread_cond_timedwait()对系统时钟操作具有鲁棒性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下与POSIX完全兼容的源代码:

Consider the following source code, which is fully POSIX compliant:

#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;
}

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

Prints the current system date every 5 seconds, however, it does a sleep of 3 seconds between getting the current system time (gettimeofday) and the condition wait (pthread_cond_timedwait).

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

Right after it is printing "sleep (...)", try setting the system clock two days into the past. What happens? Well, instead of waiting 2 more seconds on the condition as it usually does, pthread_cond_timedwait now waits for two days and 2 seconds.

该如何解决?
如何编写POSIX兼容代​​码,当用户操作系统时钟时该代码不会中断?

How do I fix that?
How can I write POSIX compliant code, that does not break when the user manipulates the system clock?

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

Please keep in mind that the system clock might change even without user interaction (e.g. a NTP client might update the clock automatically once a day). Setting the clock into the future is no problem, it will only cause the sleep to wake up early, which is usually no problem and which you can easily "detect" and handle accordingly, but setting the clock into the past (e.g. because it was running in the future, NTP detected that and fixed it) can cause a big problem.

PS:
我的系统上既没有pthread_condattr_setclock()也没有CLOCK_MONOTONIC.这些是POSIX 2008规范(基础"的一部分)所必需的,但是到目前为止,大多数系统仍仅遵循POSIX 2004规范,而在POSIX 2004规范中,这两个是可选的(高级实时扩展).

PS:
Neither pthread_condattr_setclock() nor CLOCK_MONOTONIC exists on my system. Those are mandatory for the POSIX 2008 specification (part of "Base") but most systems still only follow the POSIX 2004 specification as of today and in the POSIX 2004 specification these two were optional (Advanced Realtime Extension).

推荐答案

有趣的是,我以前没有遇到过这种行为,但是再说一次,我不习惯于浪费我的系统时间:- )

Interesting, I've not encountered that behaviour before but, then again, I'm not in the habit of mucking about with my system time that much :-)

假设您这样做是出于正当的理由,一种可能的解决方案(虽然麻烦)是让另一个线程的唯一目的是定期踢条件变量以唤醒任何受影响的线程.

Assuming you're doing that for a valid reason, one possible (though kludgy) solution is to have another thread whose sole purpose is to periodically kick the condition variable to wake up any threads so affected.

换句话说,是这样的:

while (1) {
    sleep (10);
    pthread_cond_signal (&condVar);
}

您等待条件变量被踢的代码无论如何都要检查其谓词(以保护虚假的唤醒),这样就不会对功能产生任何实际的不利影响.

Your code that's waiting for the condition variable to be kicked should be checking its predicate anyway (to take care of spurious wakeups) so this shouldn't have any real detrimental effect on the functionality.

对性能的影响不大,但是每十秒钟一次应该不是太大的问题.真正的目的只是要照顾您的定时等待(无论出于何种原因)将等待很长时间的情况.

It's a slight performance hit but once every ten seconds shouldn't be too much of a problem. It's only really meant to take care of the situations where (for whatever reason) your timed wait will be waiting a long time.

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

Another possibility is to re-engineer your application so that you don't need timed waits at all.

在由于某种原因需要唤醒线程的情况下,它总是由另一个线程完全替代,该线程完全有能力触发条件变量来唤醒一个线程(或广播以唤醒很多线程).

In situations where threads need to be woken for some reason, it's invariably by another thread which is perfectly capable of kicking a condition variable to wake one (or broadcasting to wake the lot of them).

这与我上面提到的踢线非常相似,但更多的是作为体系结构不可或缺的一部分,而不是用螺栓固定.

This is very similar to the kicking thread I mentioned above but more as an integral part of your architecture than a bolt-on.

这篇关于如何使pthread_cond_timedwait()对系统时钟操作具有鲁棒性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆