1

我正在编写一个简单的程序,它检查经过的时间是否超过 1 秒。我使用clock_gettime() 获取开始时间,然后调用sleep(5),获取新时间并检查差异是否大于1;我睡了 5 秒,然后它应该大于 5,但是我的程序打印了一个奇怪的结果。

这是代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

int main()
{
    struct timespec tv1,tv3,expected;
    struct timespec nano1;

    tv1.tv_nsec = 0;
    tv3.tv_nsec = 0;

    expected.tv_nsec = 400000;

    if(clock_gettime(CLOCK_MONOTONIC,&tv3) == -1){
        perror(NULL);
    }

    sleep(5);

    if(clock_gettime(CLOCK_MONOTONIC,&tv1) == -1){
        perror(NULL);
    }


    long nsec = tv1.tv_nsec - tv3.tv_nsec;

    if(nsec>expected.tv_nsec){
        printf("nsec runned: %ld   nsec timeout: %ld\n",nsec,expected.tv_nsec);
    }


    printf("elapsed nanoseconds: %ld\n",nsec);


    if((nsec>expected.tv_nsec))
        printf("expired timer\n");

    else
        printf("not expired timer\n");

    exit(EXIT_SUCCESS);
}

我的程序的输出是:

“经过的纳秒:145130”和“未过期的超时”

问题出在哪里?

4

2 回答 2

4

a 中表示的时间struct timespec有两个组成部分:

  • tv_sectime_t整数秒数的值。
  • tv_nsec— 纳秒数的 32 位整数,0..999,999,999

您的计算没有考虑值之间的差异tv_sec。纳秒值之间的差异与您所说的一样大,这有点令人惊讶,但远非不可能。要获得全部差异,您需要同时考虑 thetv_sectv_nsec组件。

sub_timespec()

您可以使用以下函数减去两个值(以获得差异):

enum { NS_PER_SECOND = 1000000000 };

void sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td)
{
    td->tv_nsec = t2.tv_nsec - t1.tv_nsec;
    td->tv_sec  = t2.tv_sec - t1.tv_sec;
    if (td->tv_sec > 0 && td->tv_nsec < 0)
    {
        td->tv_nsec += NS_PER_SECOND;
        td->tv_sec--;
    }
    else if (td->tv_sec < 0 && td->tv_nsec > 0)
    {
        td->tv_nsec -= NS_PER_SECOND;
        td->tv_sec++;
    }
}

fmt_timespec

您可以使用如下函数将其格式化为具有指定小数位数的浮点值:

int fmt_timespec(const struct timespec *value, int dp, char *buffer, size_t buflen)
{
    assert(value != 0 && buffer != 0 && buflen != 0);
    if (value == 0 || buffer == 0 || buflen == 0)
    {
        errno = EINVAL;
        return -1;
    }
    assert(dp >= 0 && dp <= 9);
    if (dp < 0 || dp > 9)
    {
        errno = EINVAL;
        return -1;
    }
    if ((value->tv_sec > 0 && value->tv_nsec < 0) ||
        (value->tv_sec < 0 && value->tv_nsec > 0))
    {
        /* Non-zero components of struct timespec must have same sign */
        errno = EINVAL;
        return -1;
    }

    int len;
    if (dp == 0)
        len = snprintf(buffer, buflen, "%ld", value->tv_sec);
    else
    {
        long nsec = value->tv_nsec;
        long secs = value->tv_sec;
        const char *sign = (secs < 0 || (secs == 0 && nsec < 0)) ? "-" : "";
        if (secs < 0)
            secs = -secs;
        if (nsec < 0)
            nsec = -nsec;
        for (int i = 0; i < 9 - dp; i++)
            nsec /= 10;
        len = snprintf(buffer, buflen, "%s%ld.%.*ld", sign, secs, dp, nsec);
    }
    if (len > 0 && (size_t)len < buflen)
        return len;
    errno = EINVAL;
    return -1;
}

有问题的代码的修订版本

标头time_io.h声明了 ; 的格式和扫描功能struct timespec。头time_math.h文件声明了用于加减值的struct timespec函数。有这么多标题可能是过度划分代码。

#include "time_io.h"
#include "time_math.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

enum { NS_PER_SECOND = 1000000000 };

int main(void)
{
    struct timespec tv3;
    if (clock_gettime(CLOCK_MONOTONIC, &tv3) == -1)
        perror("clock_gettime()");

    sleep(5);

    struct timespec tv1;
    if (clock_gettime(CLOCK_MONOTONIC, &tv1) == -1)
        perror("clock_gettime()");

    struct timespec td;
    sub_timespec(tv3, tv1, &td);

    int64_t ts_in_ns = td.tv_sec * NS_PER_SECOND + td.tv_nsec;

    char buffer[32];
    fmt_timespec(&td, 9, buffer, sizeof(buffer));

    printf("Elapsed time: %s (%" PRId64 " nanoseconds)\n", buffer, ts_in_ns);

    return 0;
}

示例运行:

Elapsed time: 5.005192000 (5005192000 nanoseconds)

在运行 macOS Sierra 10.12.6 的 Mac 上(终于有了——Mac clock_gettime()OS X 的早期版本不支持它),分辨率clock_gettime()为 1000 纳秒,实际上是微秒。因此,最后 3 位小数在 Mac 上始终为零。

add_timespec()

为了完整起见,您可以添加两个struct timespec值:

void add_timespec(struct timespec t1, struct timespec t2, struct timespec *td)
{
    td->tv_nsec = t2.tv_nsec + t1.tv_nsec;
    td->tv_sec  = t2.tv_sec + t1.tv_sec;
    if (td->tv_nsec >= NS_PER_SECOND)
    {
        td->tv_nsec -= NS_PER_SECOND;
        td->tv_sec++;
    }
    else if (td->tv_nsec <= -NS_PER_SECOND)
    {
        td->tv_nsec += NS_PER_SECOND;
        td->tv_sec--;
    }
}

scn_timespec()

而且“扫描”过程更加混乱(输入通常比输出更加混乱):

int scn_timespec(const char *str, struct timespec *value)
{
    assert(str != 0 && value != 0);
    if (str == 0 || value == 0)
    {
        errno = EINVAL;
        return -1;
    }
    long sec;
    long nsec = 0;
    int sign = +1;
    char *end;
    /* No library routine sets errno to 0 - but this one needs to */
    int old_errno = errno;

    errno = 0;

    /* Skip leading white space */
    while (isspace((unsigned char)*str))
        str++;

    /* Detect optional sign */
    if (*str == '+')
        str++;
    else if (*str == '-')
    {
        sign = -1;
        str++;
    }

    /* Next character must be a digit */
    if (!isdigit((unsigned char)*str))
    {
        errno = EINVAL;
        return -1;
    }

    /* Convert seconds part of string */
    sec = strtol(str, &end, 10);
    if (end == str || ((sec == LONG_MAX || sec == LONG_MIN) && errno == ERANGE))
    {
        errno = EINVAL;
        return -1;
    }

    if (*end != '\0' && !isspace((unsigned char)*end))
    {
        if (*end++ != '.')
        {
            errno = EINVAL;
            return -1;
        }
        if (*end == '\0')
            nsec = 0;
        else if (isdigit((unsigned char)*end))
        {
            char *frac = end;
            nsec = strtol(frac, &end, 10);
            if (end == str ||
                ((nsec == LONG_MAX || nsec == LONG_MIN) && errno == ERANGE) ||
                (nsec < 0 || nsec >= NS_PER_SECOND) || (end - frac > 9))
            {
                errno = EINVAL;
                return -1;
            }
            for (int i = 0; i < 9 - (end - frac); i++)
                nsec *= 10;
        }
    }

    /* Allow trailing white space - only */
    unsigned char uc;
    while ((uc = (unsigned char)*end++) != '\0')
    {
        if (!isspace(uc))
        {
            errno = EINVAL;
            return -1;
        }
    }

    /* Success! */
    value->tv_sec = sec * sign;
    value->tv_nsec = nsec * sign;
    errno = old_errno;
    return 0;
}
于 2017-08-25T14:24:30.857 回答
0

您正在使用时间戳的 nsec(纳秒)部分,而完全忽略了 sec(秒)部分。这些时间戳由两个整数组成,要从中获得任何含义,您需要两者。

参考:http ://en.cppreference.com/w/c/chrono/timespec

于 2017-08-25T14:00:52.237 回答