我正在写一个简单的程序,它检查运行时间是否超过1秒。我用一个clock_ghtime()获取开始时间,然后调用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”和“未过期超时”
问题在哪里?

最佳答案

astruct timespec中表示的时间有两个分量:
tv_sec-整数秒数的值。
time_t-纳秒数的32位整数,0..999999999
你的计算没有考虑到tv_nsec值之间的差异。纳秒值之间的差别如你所说的那么大,这有点令人惊讶,但远不是不可能的。为了得到整体差异,您需要同时考虑tv_sectv_sec组件。
tv_nsec
您可以使用如下函数减去两个值(以获得差值):

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

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

有关准则的修订版本
headerfmt_timespec声明time_io.h的格式和扫描函数;struct timespecheader声明添加和减去time_math.h值的函数。可能是对代码进行了过度划分,使其具有如此多的头。
#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)

在运行macossierra10.12.6的Mac上(它最终有struct timespec-早期版本的Mac OS X不支持它),clock_gettime()的分辨率是1000纳秒,实际上是微秒。所以,Mac电脑的最后3位小数总是零。
clock_gettime()
为了完整起见,可以添加两个add_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--;
    }
}

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

关于c - C,clock_gettime,返回了错误的纳秒值?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45883052/

10-12 01:33