【问题标题】:Sleep C function in CodeBlocks (for Windows) doesn't work properlyCodeBlocks(适用于 Windows)中的睡眠 C 函数无法正常工作
【发布时间】:2016-09-27 09:33:14
【问题描述】:

我正在尝试用 C 语言(适用于 Windows)做一个秒表,代码似乎可以工作,但睡眠功能的时间与实时不匹配。

进程返回 0 (0x0) 执行时间:1.907 秒 按任意键继续。

问题是执行时间大约是 2 秒,但应该只有 1 秒。只是想知道我做错了什么,因为 Windows 中的睡眠功能接受毫秒作为它应该工作的参数。这是代码

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{

    int milliseconds=0;
    int seconds=0;
    int counter;

    for(counter=0;counter<1000;counter++) {

        Sleep(1);
        milliseconds = milliseconds + 1;
        printf("%d\n",milliseconds);

        if(milliseconds==1000) {
            seconds = seconds + 1;
            milliseconds = 0;
            printf("seconds: %d",seconds);
        }

    }

    return 0;
}

【问题讨论】:

  • 你得到哪个输出?
  • 输出为:998 999 1000 秒:1 进程返回 0 (0x0) 执行时间:1.898 s 按任意键继续。
  • 您在测量时遇到了一个常见错误:您将单个测量值相加。每个都会带来一个可能很小的特定错误。但是,将单个结果相加也会增加错误。尝试在这里测量绝对值。
  • 每个循环休眠一毫秒,然后做它的事情,所以每次迭代是 1ms 加上“做事情的时间”。完成这 1000 次最终需要 1000 毫秒 plus 1000 * “做事的时间”。
  • 这个“printf("%d\n",milliseconds);”实际上打印的是计数迭代,而不是毫秒。

标签: c windows codeblocks sleep


【解决方案1】:

您正在休眠,超时时间为 1 毫秒。实际上,您放弃了当前线程的时间片,默认情况下为 15.6 毫秒。但是如果你有一个像 Visual Studio 一样运行的 WPF 应用程序,它将被设置为 1ms。睡眠将不会早于您想要睡眠的时间返回,因此您将有效地等待最多 2 秒睡眠时间的时间片。 如果你使用像ETWController 这样的分析器,你可以看到线程直接等待。

您会看到,我们有 1004 个上下文切换事件,它们平均等待了 1.6 毫秒,而不是您预期的 1 毫秒。操作系统调度程序如何影响您的睡眠时间还有很多。最好的办法是测量。参见例如SpinWait is dangerous

当我例如关闭所有强制执行 1ms 系统计时器的应用程序我会得到 6.5 秒的睡眠时间! 为了检查真正的等待时间,我使用了带有高分辨率计时器的代码来打印真正的等待时间:

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <chrono>

int main()
{

    int milliseconds = 0;
    int seconds = 0;
    int counter;

    auto start = std::chrono::high_resolution_clock::now();

    for (counter = 0; counter<1000; counter++) {

        Sleep(1);
        milliseconds = milliseconds + 1;
        //printf("%d\n", milliseconds);

        if (milliseconds == 1000) {
            seconds = seconds + 1;
            milliseconds = 0;
            printf("\nseconds: %d", seconds);
        }

    }

    auto stop = std::chrono::high_resolution_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
    printf("Duration: %dms", ms);
    return 0;
}

这是输出:

ClockRes v2.0 - View the system clock resolution
Copyright (C) 2009 Mark Russinovich
SysInternals - www.sysinternals.com

Maximum timer interval: 15.625 ms
Minimum timer interval: 0.500 ms
Current timer interval: 1.000 ms
seconds: 1
Duration: 1713ms

ClockRes v2.0 - View the system clock resolution
Copyright (C) 2009 Mark Russinovich
SysInternals - www.sysinternals.com

Maximum timer interval: 15.625 ms
Minimum timer interval: 0.500 ms
Current timer interval: 15.625 ms

seconds: 1
Duration: 6593ms

【讨论】:

    【解决方案2】:

    您不能使用Sleep 函数编写秒表。它不是用来计时的。它所做的只是使线程将其剩余的时间片让给其他竞争线程,从而允许它们执行。无法保证您的线程将休眠的确切时间。它的执行可能会被更高优先级的线程抢占。根据the documentation

    如果 dwMilliseconds 小于系统时钟的分辨率,线程可能会休眠少于指定的时间长度。如果 dwMilliseconds 大于 1 刻但小于 2,则等待时间可以介于 1 到 2 刻之间,以此类推。

    它继续讨论如何提高睡眠间隔的准确性,但这也不是您想要的。创建一个计时器会更适合您的目的,例如使用SetTimer function。指定一个回调函数,时间到了你会收到通知。如果您需要一个非常准确的计时器,您可以使用multimedia timer。教程可用here。不过,对于简单的秒表来说可能是不必要的。

    要获取时间计数并实现您自己的计时功能,您可以调用GetTickCount 函数。或使用high-resolution timer APIs 获得最大分辨率。 QueryPerformanceCounter 返回当前时间戳,除以 QueryPerformanceFrequency 的结果。

    【讨论】:

      【解决方案3】:

      系统会尽最大努力处理对操作系统的请求,不保证任何响应时间。 Windows 的设计和优化考虑了性能和吞吐量,用于一般用途,而不是实时任务。您的进程与其他需要操作系统注意的进程共享操作系统。操作系统仅保证进程暂停将持续至少,只要您请求给予或接受时钟滴答。加上循环处理时间意味着您将从该代码中得到不一致的结果。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-10-27
        • 2015-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-15
        • 1970-01-01
        相关资源
        最近更新 更多