C# Thread.Sleep malfunction

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

It seems that on some XP platforms the Thread.Sleep method is malfunctioning.
I created a small application that was supposed to show 2 newstickerbars on
the main form, with the added possibility to alter the speed with which they
tick.

To achieve this I use a thread that handles the actual drawing and movement
of the ticker: each tick the target object (in this case a panel) is being
shifted 1 pixel to the left, and the next line is copied onto the right of
the panel (I use a combination of BitBlt and DrawImage to do this).

To control the speed I put the threads to sleep with a delay of around 5
milliseconds, which seems to give a nice effect. However on some systems
(specially XP) when there are 2 tickerbars on the main form, one having a
sleep time of 4 ms and the other having a sleep time of 5 ms, there is NO
visual speed difference: they move at the same speed. On windows2000
professional everything seems to work fine. Does anybody know what is going
wrong here?
(I can give you the sourcecode if needed...)
 
You have presumed the time for sleep is exact. In fact, it is only the
minimum time until your thread runs again. Since XP normally runs with
approximately a 10 millisecond scheduler, your 4 and 5 millisecond requests
are almost certainly both 10. Sleep is no good for fine grained timing.
Bob Milton
 
Hmm. So they decided to downgrade the thread accuracy....? very strange since
10 milliseconds is quite a long time. Aren't you confused with normal
WM_TIMER timer. That one has an accuracy of around 10 milliseconds.
 
No they didn't, but your thread is not the only runable thread in the system
I guess.
So if you specify a sleep period of 4 msec. that means your thread will
sleep for 4 msec after which he enters the ready state, any running thread
remains running until he is preempted and the scheduler activates another
(not necessarily and most certainly not yours ) thread from the ready queue.
As an example - say you have 10 threads waiting in the queue to be serviced
before your thread, and each thread runs for an average of 2msec. The result
is that your thread effectively "sleeps" 20 msec.

Willy.
 
On XP, Thread.Sleep's default granularity follows that of the system timer.
To get more accurate results from Thread.Sleep, you must lower the default
timer resolution e.g. to 1 millisecond. There doesn't appear to be a managed
way to do that, but fortunately, importing the relevant WIN32 functionality
is quite simple (outline):

[DllImport("winmm.dll")]
internal static extern uint timeBeginPeriod(uint period);

[DllImport("winmm.dll")]
internal static extern uint timeEndPeriod(uint period);

{

Thread.Sleep(1); // wait's roughtly 15ms on my system
Thread.Sleep(2); // wait's roughtly 15ms on my system
Thread.Sleep(3); // wait's roughtly 15ms on my system

timeBeginPeriod(1);

Thread.Sleep(1); // wait's just over 1 ms on my system
Thread.Sleep(2); // wait's just over 2 ms on my system
Thread.Sleep(3); // wait's just over 3 ms on my system

timeEndPeriod(1);

Thread.Sleep(1); // wait's roughtly 15ms on my system
Thread.Sleep(2); // wait's roughtly 15ms on my system
Thread.Sleep(3); // wait's roughtly 15ms on my system

}

IHTH

- Per
 
Per ,

I'm sorry, but this is not correct, Thread.Sleep uses OS Kernel Waitable
timer objects who have a resolution of 1 msec., It's not using the
multimedia timer service nor the system timer.

If this were true , following
timeBeginPeriod(10000);
Thread.Sleep(1);
would sleep 10 sec.

I wonder how you measured the sleep time on your system (sepecially those
below 10 msec).

Willy.
 
Willy,
I'm sorry, but this is not correct, Thread.Sleep uses OS Kernel Waitable
timer objects who have a resolution of 1 msec.

Not on my system (XP/Pro/sp1) (the resolution, that is - I don't claim to
know how it's actually implemented).
If this were true , following
timeBeginPeriod(10000);
Thread.Sleep(1);
would sleep 10 sec.

I haven't tried it for anything above 50 mSecs, or so, so you could be
right. Isn't there an upper limit on what timeBeginPeriod supports anyway?
I wonder how you measured the sleep time on your system (sepecially those
below 10 msec).

QueryPerformanceCounter, but I won't keep you guessing at what I'm doing :)
You'll find my test code (that happened to still have lying around) below.
Try running the test as it is first, then comment out the call to
timeBeginPeriod and run it again.

Here are my results. Req is the argument to Sleep, Avg, Max, and Min are the
measured results.

With call to timeBeginPeriod:

Req:0 Avg:0.004 Max:0.256 Min:0
Req:1 Avg:1.96 Max:2.551 Min:1.613
Req:2 Avg:3.005 Max:11.561 Min:2.11
Req:3 Avg:3.904 Max:4.074 Min:3.743
Req:4 Avg:4.88 Max:4.917 Min:4.787
Req:5 Avg:5.857 Max:5.869 Min:5.768
Req:6 Avg:6.833 Max:6.933 Min:6.735
Req:7 Avg:7.81 Max:7.881 Min:7.711
Req:8 Avg:8.786 Max:8.823 Min:8.692
Req:9 Avg:9.763 Max:9.971 Min:9.565
Req:10 Avg:10.739 Max:10.749 Min:10.642
Req:11 Avg:11.716 Max:11.754 Min:11.617
Req:12 Avg:12.692 Max:12.708 Min:12.585
Req:13 Avg:13.669 Max:13.768 Min:13.569
Req:14 Avg:14.645 Max:14.657 Min:14.551
Req:15 Avg:15.622 Max:15.635 Min:15.533
Req:16 Avg:16.598 Max:16.68 Min:16.515
Req:17 Avg:17.575 Max:17.615 Min:17.483
Req:18 Avg:18.551 Max:18.57 Min:18.459
Req:19 Avg:19.528 Max:19.538 Min:19.426
Press <CR>

Without call to timeBeginPeriod:

Req:0 Avg:0.004 Max:0.258 Min:0
Req:1 Avg:15.541 Max:16.899 Min:7.66
Req:2 Avg:15.621 Max:15.788 Min:15.456
Req:3 Avg:15.622 Max:15.785 Min:15.464
Req:4 Avg:15.622 Max:15.811 Min:15.433
Req:5 Avg:15.622 Max:15.99 Min:15.264
Req:6 Avg:15.622 Max:15.719 Min:15.525
Req:7 Avg:15.622 Max:15.722 Min:15.523
Req:8 Avg:15.622 Max:15.956 Min:15.294
Req:9 Avg:15.622 Max:15.721 Min:15.526
Req:10 Avg:15.622 Max:15.721 Min:15.533
Req:11 Avg:15.622 Max:15.954 Min:15.304
Req:12 Avg:15.623 Max:15.724 Min:15.537
Req:13 Avg:15.621 Max:16.023 Min:15.291
Req:14 Avg:15.622 Max:15.93 Min:15.33
Req:15 Avg:15.622 Max:15.874 Min:15.373
Req:16 Avg:31.246 Max:31.512 Min:30.915
Req:17 Avg:31.246 Max:31.324 Min:31.128
Req:18 Avg:31.246 Max:31.581 Min:30.923
Req:19 Avg:31.246 Max:31.341 Min:31.16
Press <CR>

If I'm doing something wrong here, I'll be very interested to learn what it
is.
My system is a Dell 8300 P4@3GHz, in case that matters. Hyperthreading is
not enabled.
Oh, and I'm running this from within VS.NET1.1.

- Per


Waiter.cs:
-------------------------------------------------
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace WaitTest
{
/// <summary>
/// Summary description for Waiter.
/// </summary>
public class Waiter
{
internal const String WINMM = "winmm.dll";
internal const String KERNEL32 = "kernel32.dll";
[DllImport(WINMM)]
internal static extern uint timeBeginPeriod(
uint period);
[DllImport(WINMM)]
internal static extern uint timeEndPeriod(
uint period);
[DllImport(KERNEL32)]
internal static extern bool QueryPerformanceCounter(
out long PerformanceCount);
[DllImport(KERNEL32)]
internal static extern bool QueryPerformanceFrequency(
out long Frequency);
internal static long Freq;
static Waiter()
{
timeBeginPeriod(1);
QueryPerformanceFrequency(out Freq);
}
~Waiter()
{
timeEndPeriod(1);
}
static public long GetTimestamp()
{
long result;
QueryPerformanceCounter(out result);
return result;
}
static public long DeltaMilliseconds(long earlyTimestamp, long
lateTimestamp)
{
return (((lateTimestamp - earlyTimestamp) * 1000) / Freq);
}
static public long DeltaMicroseconds(long earlyTimestamp, long
lateTimestamp)
{
return (((lateTimestamp - earlyTimestamp) * 1000000) / Freq);
}
static public void Wait(int delay)
{
long Elapsed = 0;
long Before = GetTimestamp();
while (delay > Elapsed)
{
System.Threading.Thread.Sleep(delay);
Elapsed = DeltaMilliseconds(Before, GetTimestamp());
}
}
}
}
-------------------------------------------------

Class1.cs:
-------------------------------------------------
using System;
namespace WaitTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
int MaxDelay = 20;
int MaxLoopCount = 100;
for (int delay = 0; delay < MaxDelay; delay++)
{
long Total = 0;
long Max = 0;
long Min = 0;
for (int loopCount = 0; loopCount < MaxLoopCount; loopCount++)
{
long start = Waiter.GetTimestamp();
Waiter.Wait(delay);
long end = Waiter.GetTimestamp();
long actualDelay = Waiter.DeltaMicroseconds(start, end);
if (loopCount == 0)
{
Max = actualDelay;
Min = actualDelay;
}
else
{
if (actualDelay > Max)
Max = actualDelay;
if (actualDelay < Min)
Min = actualDelay;
}
Total += actualDelay;
}
Console.WriteLine("Req:{0} Avg:{1} Max:{2} Min:{3}",
delay, Total / MaxLoopCount / 1000.0, Max / 1000.0, Min / 1000.0);
}
Console.WriteLine("Press <CR>");
Console.ReadLine();
}
}
}
-------------------------------------------------
 
I have been experiencing the same problem, it seems to depend on the machine. We are using the beta 2.0 version of .net on XP. In a loop write a small program which calls thread.sleep(ii) in a loop where ii goes from say 0, to 100 milliseconds by 1 mill. and observe the actual time of the sleep. Using visual c++ 6.0 (before .net) the actual sleep was almost always rouned up to the nearest 15 mills but in .net it may or may not round up. (I think 15 mills is the timeslice)
I am blaming the .net beta but others are saying there are fast computers and slow ones.


**********************************************************************
Sent via Fuzzy Software @ http://www.fuzzysoftware.com/
Comprehensive, categorised, searchable collection of links to ASP & ASP.NET resources...
 
Jim Happ said:
I have been experiencing the same problem, it seems to depend on the machine. We are using the beta 2.0 version of .net on XP. In a loop write a small program which calls thread.sleep(ii) in a loop where ii goes from say 0, to 100 milliseconds by 1 mill. and observe the actual time of the sleep. Using visual c++ 6.0 (before .net) the actual sleep was almost always rouned up to the nearest 15 mills but in .net it may or may not round up. (I think 15 mills is the timeslice)
I am blaming the .net beta but others are saying there are fast computers and slow ones.


**********************************************************************
Sent via Fuzzy Software @ http://www.fuzzysoftware.com/
Comprehensive, categorised, searchable collection of links to ASP & ASP.NET resources...
 
I have another theory that .net's Thread.Sleep() depends on if the .net SDK
has been installed.

If anyone is interested, here is my VC++ 6.0 code and output which (so far)
always behaves the same on XP regardless of .net version or SDK installation.
CODE:
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows
headers
#include <stdio.h>
#include <windows.h>
int main(int argc, char* argv[])
{
printf("Hello World!\n");
for (int is=0; is<100; is++) {
int t1 = GetTickCount();
Sleep(is);
int t2 = GetTickCount();
printf("Sleep(%d) took %d mills\n", is, t2-t1);
}
return 0;
}

OUTPUT:
Hello World!
Sleep(0) took 0 mills
Sleep(1) took 16 mills
Sleep(2) took 15 mills
Sleep(3) took 16 mills
Sleep(4) took 15 mills
Sleep(5) took 16 mills
Sleep(6) took 16 mills
Sleep(7) took 15 mills
Sleep(8) took 16 mills
Sleep(9) took 16 mills
Sleep(10) took 15 mills
Sleep(11) took 16 mills
Sleep(12) took 15 mills
Sleep(13) took 16 mills
Sleep(14) took 16 mills
Sleep(15) took 15 mills
Sleep(16) took 32 mills
Sleep(17) took 31 mills
Sleep(18) took 31 mills
Sleep(19) took 31 mills
Sleep(20) took 32 mills
Sleep(21) took 31 mills
Sleep(22) took 31 mills
Sleep(23) took 31 mills
Sleep(24) took 32 mills
Sleep(25) took 31 mills
Sleep(26) took 31 mills
Sleep(27) took 31 mills
Sleep(28) took 32 mills
Sleep(29) took 31 mills
Sleep(30) took 31 mills
Sleep(31) took 31 mills
Sleep(32) took 47 mills
Sleep(33) took 47 mills
....
Sleep(92) took 94 mills
Sleep(93) took 94 mills
Sleep(94) took 109 mills
Sleep(95) took 109 mills
Sleep(96) took 110 mills
Sleep(97) took 109 mills
Sleep(98) took 110 mills
Sleep(99) took 109 mills
Press any key to continue
 
Here is a hi res version, (again in VC++ 6.0) which links statically to
winmm.lib. It also usually works within a millisecond on all machines
tested so far, thus outperforming the c#/.net.
CODE:
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers

#define USE_HIRES 1

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

int main(int argc, char* argv[])
{
int tbp = timeBeginPeriod(1);
if (tbp == TIMERR_NOERROR)
printf("TIMERR_NOERROR (success)\n");
else if (tbp == TIMERR_NOCANDO)
printf("TIMERR_NOERROR\n");

#ifdef USE_HIRES
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
double freq = (double) Frequency.QuadPart;
#endif

for (int i = 0; i<100; i++) {
#ifdef USE_HIRES
LARGE_INTEGER Count;
QueryPerformanceCounter(&Count);
double t1 = (double)Count.QuadPart / freq;
#else
double t1 = GetTickCount();
#endif
Sleep(i);
#ifdef USE_HIRES
QueryPerformanceCounter(&Count);
double t2 = (double)Count.QuadPart / freq;
#else
double t2 = GetTickCount();
#endif
printf("Sleep(%d) took %.1f ms, error=%.1f ms\n", i, 1000*(t2-t1),
1000*(t2-t1)-i);
}

timeEndPeriod(1);
getch();
return 0;
}
OUTPUT:
TIMERR_NOERROR (success)
Sleep(0) took 0.0 ms, error=0.0 ms
Sleep(1) took 14.2 ms, error=13.2 ms
Sleep(2) took 0.4 ms, error=-1.6 ms
Sleep(3) took 3.9 ms, error=0.9 ms
Sleep(4) took 4.8 ms, error=0.8 ms
Sleep(5) took 5.8 ms, error=0.8 ms
Sleep(6) took 6.8 ms, error=0.8 ms
Sleep(7) took 7.8 ms, error=0.8 ms
Sleep(8) took 8.7 ms, error=0.7 ms
Sleep(9) took 9.7 ms, error=0.7 ms
Sleep(10) took 10.7 ms, error=0.7 ms
Sleep(11) took 11.7 ms, error=0.7 ms
Sleep(12) took 12.7 ms, error=0.7 ms
Sleep(13) took 13.6 ms, error=0.6 ms
Sleep(14) took 14.6 ms, error=0.6 ms
Sleep(15) took 15.6 ms, error=0.6 ms
Sleep(16) took 16.6 ms, error=0.6 ms
Sleep(17) took 17.5 ms, error=0.5 ms
Sleep(18) took 18.5 ms, error=0.5 ms
Sleep(19) took 19.5 ms, error=0.5 ms
Sleep(20) took 20.5 ms, error=0.5 ms
Sleep(21) took 21.4 ms, error=0.4 ms
Sleep(22) took 22.4 ms, error=0.4 ms
Sleep(23) took 23.4 ms, error=0.4 ms
Sleep(24) took 24.3 ms, error=0.3 ms
....
Sleep(87) took 87.3 ms, error=0.3 ms
Sleep(88) took 88.3 ms, error=0.3 ms
Sleep(89) took 89.7 ms, error=0.7 ms
Sleep(90) took 90.7 ms, error=0.7 ms
Sleep(91) took 91.7 ms, error=0.7 ms
Sleep(92) took 92.7 ms, error=0.7 ms
Sleep(93) took 93.6 ms, error=0.6 ms
Sleep(94) took 94.6 ms, error=0.6 ms
Sleep(95) took 95.6 ms, error=0.6 ms
Sleep(96) took 96.6 ms, error=0.6 ms
Sleep(97) took 97.5 ms, error=0.5 ms
Sleep(98) took 98.5 ms, error=0.5 ms
Sleep(99) took 99.5 ms, error=0.5 ms

Jim Happ said:
I have another theory that .net's Thread.Sleep() depends on if the .net SDK
has been installed.

If anyone is interested, here is my VC++ 6.0 code and output which (so far)
always behaves the same on XP regardless of .net version or SDK installation.
CODE:
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows
headers
#include <stdio.h>
#include <windows.h>
int main(int argc, char* argv[])
{
printf("Hello World!\n");
for (int is=0; is<100; is++) {
int t1 = GetTickCount();
Sleep(is);
int t2 = GetTickCount();
printf("Sleep(%d) took %d mills\n", is, t2-t1);
}
return 0;
}

OUTPUT:
Hello World!
Sleep(0) took 0 mills
Sleep(1) took 16 mills
Sleep(2) took 15 mills
Sleep(3) took 16 mills
Sleep(4) took 15 mills
Sleep(5) took 16 mills
Sleep(6) took 16 mills
Sleep(7) took 15 mills
Sleep(8) took 16 mills
Sleep(9) took 16 mills
Sleep(10) took 15 mills
Sleep(11) took 16 mills
Sleep(12) took 15 mills
Sleep(13) took 16 mills
Sleep(14) took 16 mills
Sleep(15) took 15 mills
Sleep(16) took 32 mills
Sleep(17) took 31 mills
Sleep(18) took 31 mills
Sleep(19) took 31 mills
Sleep(20) took 32 mills
Sleep(21) took 31 mills
Sleep(22) took 31 mills
Sleep(23) took 31 mills
Sleep(24) took 32 mills
Sleep(25) took 31 mills
Sleep(26) took 31 mills
Sleep(27) took 31 mills
Sleep(28) took 32 mills
Sleep(29) took 31 mills
Sleep(30) took 31 mills
Sleep(31) took 31 mills
Sleep(32) took 47 mills
Sleep(33) took 47 mills
...
Sleep(92) took 94 mills
Sleep(93) took 94 mills
Sleep(94) took 109 mills
Sleep(95) took 109 mills
Sleep(96) took 110 mills
Sleep(97) took 109 mills
Sleep(98) took 110 mills
Sleep(99) took 109 mills
Press any key to continue
 
In XP Pro I have found Sleep() has a 15ms resolution as the default and
GetTickCount() has a 1ms resolution by default.

If you are running on a Dell with all the stupid multimedia software they
install for you, often some proccess is running which has called
timeBeginPeriod(1). This reverses the numbers just quoted. Listing to music
with Windows Media Player does this as well. But this is not the default
behavior.

Fortunately, the performance counter routines seem independent of this
nonsense.
 
Back
Top