Is usleep() in C implemented as busy wait?

2020-06-09 11:38发布

问题:

I'm building a multithreaded application with pthreads and need a thread to periodically check some stuff. During the time in between this thread shouldn't use any CPU. Is this possible with usleep()? Is usleep() not busy waiting? Or is there a better solution?

回答1:

The function usleep has been removed from SUSv4. You should probably use nanosleep instead or timers (setitimer, etc).

As R.. notes in the comments, should the sleep be implemented as a busy wait:

  • The thread would continue to use the CPU
  • Other (lower-priority) threads wouldn't get a chance to run

Thus:

  • Some might use signals (I think SUSv3 mentioned SIGALARM?)
  • Some might use fancy timers


回答2:

(usleep is not part of the C standard, but of an ancient POSIX standard. But see below.)

No, the POSIX specification of usleep clearly states

The usleep() function will cause the calling thread to be suspended from execution ...

so this clearly requires that it suspends execution and lets the resources to other processes or threads.

As already be mentioned by others, the POSIX function nanosleep is now replacing usleep and you should use that. C (since C11) has a function thrd_sleep that is modeled after nanosleep.



回答3:

Just be aware that both usleep() and nanosleep() can be interrupted by a signal. nanosleep() lets you pass in an extra timespec pointer where the remaining time will be stored if that happens. So if you really need to guarantee your delay times, you'll probably want to write a simple wrapper around nanosleep().

Beware that this is not tested, but something along these lines:

int myNanoSleep(time_t sec, long nanosec)
{
   /* Setup timespec */
   struct timespec req;
   req.tv_sec = sec;
   req.tv_nsec = nanosec;

   /* Loop until we've slept long enough */
   do 
   {
      /* Store remainder back on top of the original required time */
      if( 0 != nanosleep( &req, &req ) )
      {
          /* If any error other than a signal interrupt occurs, return an error */
          if(errno != EINTR)
             return -1; 
      }
      else
      {
          /* nanosleep succeeded, so exit the loop */
          break;
      }
   } while( req.tv_sec > 0 || req.tv_nsec > 0 )
   return 0; /* Return success */
}

And if you ever need to wake the thread for something other than a periodic timeout, take a look at condition variables and pthread_cond_timedwait().



回答4:

On Linux, it is implemented with the nanosleep system call which is not a busy wait.

Using strace, I can see that a call to usleep(1) is translated into nanosleep({0, 1000}, NULL).



回答5:

usleep() is a C runtime library function built upon system timers.
nanosleep() is a system call.

Only MS-DOS, and like ilk, implement the sleep functions as busy waits. Any actual operating system which offers multitasking can easily provide a sleep function as a simple extension of mechanisms for coordinating tasks and processes.



回答6:

It's worth mentioning that Wine (lazily?) implements usleep as a call to select():

#ifndef HAVE_USLEEP
int usleep (unsigned int useconds)
{
#if defined(__EMX__)
    DosSleep(useconds);
    return 0;
#elif defined(__BEOS__)
    return snooze(useconds);
#elif defined(HAVE_SELECT)
    struct timeval delay;

    delay.tv_sec = useconds / 1000000;
    delay.tv_usec = useconds % 1000000;

    select( 0, 0, 0, 0, &delay );
    return 0;
#else /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */
    errno = ENOSYS;
    return -1;
#endif /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */
}
#endif /* HAVE_USLEEP */

It's also worth mentioning that nanosleep (in glibc) appears to be little more than an empty function call (anything else, and the delay might shift into the microsecond range).

/* Pause execution for a number of nanoseconds.  */
int
__libc_nanosleep (const struct timespec *requested_time,
                  struct timespec *remaining)
{
  __set_errno (ENOSYS);
  return -1;
}


标签: c posix