Question

I have an infinite loop:-

while(true)
{
//display an image
}

Obviously the CPU goes up.

I have used:

Thread.Sleep(100);
Thread.Sleep(0);
ApplicationDoEvents() - I know i shouldn't

This loop will never end unless the application is ended. Are they any other alternatives to an infinite loop?

I am displaying the image in a usercontrol and I am overriding the OnPaint event...

if (CurrentFrame != null)
{
    g.DrawImageUnscaled(CurrentFrame, 0,0);
}



public void NewFrame(Image _currentFrame)
{
    if (CurrentFrame != null)
    {
        CurrentFrame.Dispose();
    }
    CurrentFrame = _currentFrame;
    Invalidate();
    //Update();  
}

Any insights/suggestions/recommendations would be most welcome...

Was it helpful?

Solution 2

The normal thing to do when you need a thread to sleep until some condition is true is to use a condition variable. The thread locks a mutex and then passes it to a condition variable and blocks (and the lock on the mutex is then temporarily released). Then, when the condition that it's waiting for is true, the thread that made that condition true signals the condition variable, and that thread wakes up, reacquiring the lock on the mutex, and continues on its merry way. The thing to watch out for though is that condition variables can have spurious wake-ups (and sometimes multiple threads can be waiting on the same condition), so there needs to be a flag of some sort to indicate when the condition is true, and when the thread wakes up from the condition variable, it checks that flag and blocks on the condition variable again if the flag isn't true. The mutex that is being used with the condition variable is also used to protect that flag.

The D runtime has a condition variable in core.sync.condition.

There are also other good questions relating to condition variables on stack overflow such as this one: condition variable

Now, as for your particular use case, I have to concur with the comment by Idan Arye that it seems a bit off that you would be trying to do this with painting with a windowing toolkit. Normally, the way that that works is that you override the OnPaint function (or whatever that particular toolkit calls it) and then the windowing toolkit calls the function for you. You don't tell it to paint yourself or have to worry about waiting for a condition or anything like that. So, it sounds like you may be misunderstanding how to use the windowing toolkit that you're using.

OTHER TIPS

There are a couple of things you can do here. The first is to do updates at a standard rate, using a timer. If you're using Windows Forms, you can drop a Timer component on your form and set the period to, say 15 ms. That'll give you (approximately) 66 frame updates per second.

Note that the .NET timers will give you, at best, about 15 ms resolution. You could roll your own implementation of one of the Windows timers to give you 1 ms resolution, but doing so will greatly complicate your code.

The other thing you can do is have the code that updates the CurrentFrame image call the update method, and have that update method not update more often than some set rate. You do this by starting a Stopwatch at the beginning of your program, and checking the time elapsed since the last update. Something like:

private Stopwatch FrameTimer = Stopwatch.StartNew();
private long LastUpdateTime = 0;
private const long MinUpdateMs = 10; // minimum time between updates

void DoUpdate()
{
    long currentTime = FrameTimer.ElapsedMilliseconds;
    if ((currentTime - LastUpdateTime) < MinUpdateMs)
    {
        // updated within the last 10 ms
        return;
    }
    LastUpdateTime = currentTime;
    if (CurrentFrame != null)
    {
        g.DrawImageUnscaled(CurrentFrame, 0,0);
    }
}

The second option is very effective, although you run the risk of not getting the last update if there isn't continual change. If there can be a long period between updates to your CurrentFrame, then you can have a timer that forces an update every second or so. If you do that, you want to add some synchronization in the DoUpdate method to prevent concurrent updates. I would suggest:

private object UpdateLock = new object();
void DoUpdate()
{
    if (!Monitor.TryEnter(UpdateLock))
    {
        // update already in progress
        return;
    }
    try
    {
        long currentTime = FrameTimer.ElapsedMilliseconds;
        if ((currentTime - LastUpdateTime) < MinUpdateMs)
        {
            // updated within the last 10 ms
            return;
        }
        LastUpdateTime = currentTime;
        if (CurrentFrame != null)
        {
            g.DrawImageUnscaled(CurrentFrame, 0,0);
        }
    }
    finally
    {
        Monitor.Exit(UpdateLock);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top