Waiting a Certain Amount of Time
Problem
You want to pause your program, or a single thread of it, for a specific amount of time.
Solution
The Kernel#sleep method takes a floating-point number and puts the current thread to sleep for some (possibly fractional) number of seconds:
3.downto(1) { |i| puts "#{i}…"; sleep(1) }; puts "Go!" # 3… # 2… # 1… # Go! Time.new # => Sat Mar 18 21:17:58 EST 2006 sleep(10) Time.new # => Sat Mar 18 21:18:08 EST 2006 sleep(1) Time.new # => Sat Mar 18 21:18:09 EST 2006 # Sleep for less then a second. Time.new.usec # => 377185 sleep(0.1) Time.new.usec # => 479230
Discussion
Timers are often used when a program needs to interact with a source much slower than a computer's CPU: a network pipe, or human eyes and hands. Rather than constantly poll for new data, a Ruby program can sleep for a fraction of a second between each poll, giving other programs on the CPU a chance to run. That's not much time by human standards, but sleeping for a fraction of a second at a time can greatly improve a system's overall performance.
You can pass any floating-point number to sleep, but that gives an exaggerated picture of how finely you can control a thread's sleeping time. For instance, you can't sleep for 10-50 seconds, because it's physically impossible (that's less than the Planck time). You can't sleep for Float::EPSILON seconds, because that's almost certainly less than the resolution of your computer's timer.
You probably can't even reliably sleep for a microsecond, even though most modern computer clocks have microsecond precision. By the time your sleep command is processed by the Ruby interpreter and the thread actually starts waiting for its timer to go off, some small amount of time has already elapsed. At very small intervals, this time can be greater than the time you asked Ruby to sleep in the first place.
Here's a simple benchmark that shows how long sleep on your system will actually make a thread sleep. It starts with a sleep interval of one second, which is fairly accurate. It then sleeps for shorter and shorter intervals, with lessening accuracy each time:
interval = 1.0 10.times do |x| t1 = Time.new sleep(interval) actual = Time.new - t1 difference = (actual-interval).abs percent_difference = difference / interval * 100 printf("Expected: %.9f Actual: %.6f Difference: %.6f (%.2f%%) ", interval, actual, difference, percent_difference) interval /= 10 end # Expected: 1.000000000 Actual: 0.999420 Difference: 0.000580 (0.06%) # Expected: 0.100000000 Actual: 0.099824 Difference: 0.000176 (0.18%) # Expected: 0.010000000 Actual: 0.009912 Difference: 0.000088 (0.88%) # Expected: 0.001000000 Actual: 0.001026 Difference: 0.000026 (2.60%) # Expected: 0.000100000 Actual: 0.000913 Difference: 0.000813 (813.00%) # Expected: 0.000010000 Actual: 0.000971 Difference: 0.000961 (9610.00%) # Expected: 0.000001000 Actual: 0.000975 Difference: 0.000974 (97400.00%) # Expected: 0.000000100 Actual: 0.000015 Difference: 0.000015 (14900.00%) # Expected: 0.000000010 Actual: 0.000024 Difference: 0.000024 (239900.00%) # Expected: 0.000000001 Actual: 0.000016 Difference: 0.000016 (1599900.00%)
A small amount of the reported time comes from overhead, caused by creating the second Time object, but not enough to affect these results. On my system, if I tell Ruby to sleep for a millisecond, the time spent running the sleep call greatly exceeds the time I wanted to sleep in the first place! According to this benchmark, the shortest length of time for which I can expect sleep to accurately sleep is about 1/100 of a second.
You might think to get better sleep resolution by putting the CPU into a tight loop with a certain number of repetitions. Apart from the obvious problems (this hurts system performance, and the same loop will run faster over time since computers are always getting faster), this isn't even reliable.
The operating system doesn't know you're trying to run a timing loop: it just sees you using the CPU, and it can interrupt your loop at any time, for any length of time, to let some other process use the CPU. Unless you're on an embedded operating system where you can control exactly what the CPU does, the only reliable way to wait for a specific period of time is with sleep.
Waking up early
The sleep method will end early if the thread that calls it has its run method called. If you want a thread to sleep until another thread wakes it up, use Thread.stop:
alarm = Thread.new(self) { sleep(5); Thread.main.wakeup } puts "Going to sleep for 1000 seconds at #{Time.new}…" sleep(10000); puts "Woke up at #{Time.new}!" # Going to sleep for 1000 seconds at Thu Oct 27 14:45:14 PDT 2005… # Woke up at Thu Oct 27 14:45:19 PDT 2005! alarm = Thread.new(self) { sleep(5); Thread.main.wakeup } puts "Goodbye, cruel world!"; Thread.stop; puts "I'm back; how'd that happen?" # Goodbye, cruel world! # I'm back; how'd that happen?
See Also
- Recipe 3.12, "Running a Code Block Periodically"
- Chapter 20
- The Morse Code example in Recipe 21.11, "Making Your Keyboard Lights Blink," displays an interesting use of sleep