在C中有睡眠()的替代方案吗?

时间:2008-11-05 04:33:24

标签: c sleep delay

在传统的嵌入式编程中,我们将提供类似的延迟函数:

for(i=0;i<255;i++)
   for(j=0;j<255;j++);

在微处理器的视图中,这是sleep()函数的工作原理吗?

C中是否有替代sleep()函数?

16 个答案:

答案 0 :(得分:25)

您描述的循环类型称为“忙等待”。在实际操作系统中,睡眠不会导致繁忙的等待;它告诉操作系统在睡眠期结束之前不安排进程。

答案 1 :(得分:22)

一种常见的机制是使用保证超时的select(),并将休眠时间指定为超时:

// Sleep for 1.5 sec
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 500000;
select(0, NULL, NULL, NULL, &tv);

select()通常用于检查一组文件描述符,并等待至少一个文件描述符准备好执行I / O.如果没有准备好(或者,在这种情况下,如果没有指定fds),它将超时。

select()优于繁忙循环的优势在于它在休眠时消耗的资源非常少,而繁忙的循环则在处理器的优先级允许的范围内独占。

答案 2 :(得分:12)

替代方案取决于您尝试做什么以及您使用的操作系统。

如果你只是想浪费时间,那么这些可能会有所帮助:

在大多数unix类型系统上,你会发现'usleep'功能,它或多或少像睡眠时更高分辨率。小心那个,因为它通常只能睡一个微秒。

在某些unix类型系统上,select系统调用可用于所有文件描述符集零,以便获得相当准确的亚秒等待。

在Windows系统上,你有睡眠,这几乎是相同的,但需要花费几毫秒。

在多任务操作系统中,睡眠功能有时可以作为参数给出0。这通常会导致函数放弃它的时间片,但如果没有其他任务准备好运行,则立即重新安排。

答案 3 :(得分:10)

您不会使用您发布的代码在嵌入式系统上休眠。一个不错的编译器会完全删除它,即使你的编译器没有删除它也是次优的,因为在紧密循环中运行处理器会烧毁功率,这对嵌入式系统来说是一个问题。即使没有电池运行的系统也会关注电源使用情况,因为较低的电量使用意味着更便宜的电源和冷却。

通常这样做的方法是你的CPU会实现某种IDLE或SLEEP指令,这会导致它暂时停止处理命令。连接到定时器电路的外部中断线将定期唤醒处理器,并且CPU检查它是否已经睡眠了足够长的时间,如果没有,它会重新进入休眠状态。

//Pseudo code
int start = getTime();
int end = start + sleepTime;

while (getTime() < end) {
       asm("SLEEP");
}

具体细节因处理器而异。如果您在OS上作为进程运行,则sleep调用通常只是告诉调度程序暂停您的进程,然后内核决定是安排另一个进程还是休眠CPU。此外,上述代码不适用于需要截止日期保证等的实时系统。在这些情况下,您需要在循环中获取时间,知道时间中断的持续时间,因此您知道是否可以在没有吹嘘截止日期,并可能重新编程定时器硬件或忙碌等待。

答案 4 :(得分:7)

您在OP中谈论“嵌入式编程”。如果您正在进行嵌入式工作并需要像sleep()这样的东西,通常会有硬件计数器/计时器。这将因架构而异,因此请查看数据表。

如果你没有做嵌入式工作,我道歉:)

答案 5 :(得分:6)

如果您正在使用for循环,那么您最好知道它们编译的内容以及这些指令以给定的时钟速度运行多长时间,确保CPU运行您的指令而不是其他内容(这可以在嵌入式系统中完成,但由于它不允许中断,因此很棘手)。

否则,您将无法确定实际需要多长时间。

早期的PC游戏存在这个问题 - 它们是为4.7MHz的PC而构建的,当更快的计算机出现时,它们无法播放。

“睡眠”可以工作的最佳方式是让CPU知道在任何给定点的时间。不一定是实际时间(早上7:15),但至少是相对时间(自某个时间点起8612秒)。

这样它可以将delta应用于当前时间并在循环中等待,直到达到当前+ delta。

任何依赖于CPU周期数的东西本质上都是不可靠的,因为CPU可能会转到另一个任务并让你的循环停止。

假设您有一个内存映射的16位I / O端口,CPU每秒递增一次。我们还假设它位于嵌入式系统的内存位置0x33,其中整数也是16位。一个名为sleep的函数就变成了:

void sleep (unsigned int delay) {
    unsigned int target = peek(0x33) + delay;
    while (peek(0x33) != target);
}

你必须确保peek()每次返回内存内容(因此优化编译器不会破坏逻辑)并且你的while语句每秒运行多次,所以你不会错过目标,但这些是不影响我提出的概念的操作问题。

答案 6 :(得分:6)

有关sleep()的工作原理的更多信息here

顺便说一句,忙碌的等待并不一定是业余爱好者 - 虽然它确实会烧掉您可能想要用于其他目的的处理器。如果您使用的是时间源,则仅限于该源的粒度。例如。如果你有一个1毫秒的计时器,并想要500美元,你有一个问题。如果您的嵌入式系统可以处理您将在500 uSec的循环中进行嗡嗡声的事实,那么这可能是可以接受的。即使你有一个具有所需粒度的计时器,你还需要在正确的时间从该计时器中断一个...然后调度中断处理程序...然后转到你的代码。有时繁忙的循环是最方便的解决方案。的有时

答案 7 :(得分:5)

忙碌等待即使在嵌入式系统中也是业余爱好者,使用实时源。

答案 8 :(得分:4)

任何体面的C编译器都会在没有额外工作的情况下完全删除你的代码而且延迟会消失

答案 9 :(得分:3)

sleep实际上与操作系统接口,其中休眠进程位于调度队列之外。我通常使用:

poll(0, 0, milliseconds);

适用于POSIX兼容系统。 select也适用于Windows(它们必须具有本机API(可能称为Sleep)。)

答案 10 :(得分:2)

在C#&#39;中进行线程化作者Joseph Albahari对此进行了有趣的讨论。你不能在.Net中睡眠少于1毫秒,但DateTime.Ticks的粒度为100纳秒(= 0.1微秒)。为了控制我的5轴CNC步进器,我只需要在步骤命令之间暂停10微秒。我已经使用了一个微控制器来进行令人讨厌的循环,但是如果你有一大堆话,我认为可以移交处理器来完成工作,当你可以时停止线程。至少它不会永远是同一个。

答案 11 :(得分:2)

#include <Windows.h>

static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");

static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");




static void SleepShort(float milliseconds) {
    static bool once = true;
    if (once) {
        ULONG actualResolution;
        ZwSetTimerResolution(1, true, &actualResolution);
        once = false;
    }

    LARGE_INTEGER interval;
    interval.QuadPart = -1 * (int)(milliseconds * 10000.0f);
    NtDelayExecution(false, &interval);
}

是的,它使用了一些未记录的内核函数,但老实说,它比大多数建议快得多,效果很好(注意它将限制在CPU的数量,例如我的只能睡500纳秒(0.5))

答案 12 :(得分:2)

在linux中可用 usleep(int微秒) nanosleep(...)更精确地查看调用参数的手册页

答案 13 :(得分:1)

在unix衍生的操作系统中,你可能会安排一个signal()调用,你的代码只会阻塞代码,直到信号被引发。信号用于此目的,并且它们非常简单和有效。

答案 14 :(得分:0)

尝试......真正解决这个问题,即有效的东西(不像上面的回答尝试)lol

我仍然需要改进此代码才能实现。很少有附加组件是受欢迎的。

// Sleep for both Windows and Linux: 
// Too bad? No one proposed you a solution that works? 
// Since Windows has no select.h nor poll.h, an implementation
// is necessary.
//  
// Solutions on boards are often refered to use either select or poll, but ok, what about C (not c++)?
//
/// implementation of poll is destined in this attempt for windows
/// Ideally, you add this part of code to the header of you *.c file, and it might work through...

#ifdef WIN32
#include <time.h>
#include <sys/time.h>
#include <ws2tcpip.h>
#include <Winsock2.h>
#include <windows.h>
/* winsock doesn't feature poll(), so there is a version implemented
 * in terms of select() in mingw.c. The following definitions
 * are copied from linux man pages. A poll() macro is defined to
 * call the version in mingw.c.
 */
#define POLLIN      0x0001    /* There is data to read */
#define POLLPRI     0x0002    /* There is urgent data to read */
#define POLLOUT     0x0004    /* Writing now will not block */
#define POLLERR     0x0008    /* Error condition */
#define POLLHUP     0x0010    /* Hung up */
#define POLLNVAL    0x0020    /* Invalid request: fd not open */
struct pollfd {
  SOCKET fd;        /* file descriptor */
  short events;     /* requested events */
  short revents;    /* returned events */
};

int mingw_poll (struct pollfd *, unsigned int, int);

#define poll(x, y, z)        mingw_poll(x, y, z)
#endif





int mingw_poll(struct pollfd *fds, unsigned int nfds, int timo)
{
    struct timeval timeout, *toptr;
    fd_set ifds, ofds, efds, *ip, *op;
    int i, rc;

    /* Set up the file-descriptor sets in ifds, ofds and efds. */
    FD_ZERO(&ifds);
    FD_ZERO(&ofds);
    FD_ZERO(&efds);
    for (i = 0, op = ip = 0; i < nfds; ++i) {
    fds[i].revents = 0;
    if(fds[i].events & (POLLIN|POLLPRI)) {
        ip = &ifds;
        FD_SET(fds[i].fd, ip);
    }
    if(fds[i].events & POLLOUT) {
        op = &ofds;
        FD_SET(fds[i].fd, op);
    }
    FD_SET(fds[i].fd, &efds);
    } 

    /* Set up the timeval structure for the timeout parameter */
    if(timo < 0) {
    toptr = 0;
    } else {
    toptr = &timeout;
    timeout.tv_sec = timo / 1000;
    timeout.tv_usec = (timo - timeout.tv_sec * 1000) * 1000;
    }

#ifdef DEBUG_POLL
    printf("Entering select() sec=%ld usec=%ld ip=%lx op=%lx\n",
           (long)timeout.tv_sec, (long)timeout.tv_usec, (long)ip, (long)op);
#endif
    rc = select(0, ip, op, &efds, toptr);
#ifdef DEBUG_POLL
    printf("Exiting select rc=%d\n", rc);
#endif

    if(rc <= 0)
    return rc;

    if(rc > 0) {
        for (i = 0; i < nfds; ++i) {
            int fd = fds[i].fd;
        if(fds[i].events & (POLLIN|POLLPRI) && FD_ISSET(fd, &ifds))
            fds[i].revents |= POLLIN;
        if(fds[i].events & POLLOUT && FD_ISSET(fd, &ofds))
            fds[i].revents |= POLLOUT;
        if(FD_ISSET(fd, &efds))
            /* Some error was detected ... should be some way to know. */
            fds[i].revents |= POLLHUP;
#ifdef DEBUG_POLL
        printf("%d %d %d revent = %x\n", 
                FD_ISSET(fd, &ifds), FD_ISSET(fd, &ofds), FD_ISSET(fd, &efds), 
                fds[i].revents
        );
#endif
        }
    }
    return rc;
}

答案 15 :(得分:0)

我在这篇文章中找到了这个函数(http://cboard.cprogramming.com/c-programming/111229-how-use-sleep-function.html)并且它有效:

#include <stdio.h>
#include <windows.h>

int main()
{
    puts("Hello \n");
    /* in windows.h is declared the Sleep (upper S) function and it takes time in 
miliseconds */
    Sleep(3000);
    puts("World \n");
    return 0;
}