views:

1429

answers:

5

How do you limit the CPU of a while loop?

In this case, the code which is inside the while loop:

Private Sub wait(ByVal time)
        Dim sw As New Stopwatch
        sw.Start()
        Do While sw.ElapsedMilliseconds < time And StillOpen = True
            Application.DoEvents()
        Loop
        sw.Stop()
    End Sub

But now, here is the issue. This loop is allowing the while loop to run every second, once a second, and the wait sub is causing this delay, as it should.

How can I limit the CPU that this is taking up? For some reason, my task manager says it is taking 50 CPUs to run this simple task, yet it should probably take no more than 1 or 2. Though the manager says it is taking that much CPU, my computer speed is not being affected at all, which is odd considering it is a two-year-old laptop.

I don't want any users to freak out about it, but knowing how people are these days....

Anyway, the language is vb.net. Can someone please help me?

Thanks!

EDIT: To clarify, that code is not inside the while loop itself, but a call for the subroutine is, i.e. wait(1000)

+2  A: 

You could always perform some kind of sleep between iterations of the loop...

I'm not familiar with VB.NET but a duration of 100-200ms will probably be more than enough to drop the CPU usage.

Eg:

Do while (...)
    Application.blah();
    System.Threading.Thread.Sleep(150);
End

Edit After some research, I think the function you want is: System.Threading.Thread.Sleep()

Faxwell Mingleton
Err, it actually has to wait for one second, else it will not function. It is a game, you see.
Cyclone
So, Sleep() for 1000ms then.
Michael Petrotta
It has to reload the UI while it is sleeping though.....
Cyclone
Right, but if you sleep for 100ms inside the loop that occurs 1000ms, you essentially end up just processing application events 10 times a second (or at whatever frequency you want), thus ensuring responsiveness.
Faxwell Mingleton
Name "Sleep" is not declared.
Cyclone
Updated with the proper function.
Faxwell Mingleton
Its not having any effect D:
Cyclone
If you're trying to block some work from happening and keep your UI responsive, you may need to look into other techniques, and like Joel suggested, refactor. This is just a hotfix to keep a loop from consuming all your CPU time, not a more advanced fix to cover parallel tasks (UI stuff and work). There are better answers to this question that will help you more.
Faxwell Mingleton
+7  A: 

Use a timer event !!! Nearly no cpu effort.

iDevlop
How can i change my stopwatch into a timer?
Cyclone
Correction: How can I measure the elapsed milliseconds on the timer?
Cyclone
Never use a timer to wait.
Why, roygbiv?
Cyclone
+1  A: 

Well, the CPU is always running at 100% when it's running, so the only practical way to limit the CPU usage is to run bursts or loop and sleeping in between.

Laptop CPUs usually have some SpeedStep technology or equievalent that will slow down the CPU when it's not working hard, but it's not reasonable to assume that your application would have access to control that, at least not directly. You might be able to affect it indirectly by measuring the CPU usage and adjust the length of the work and sleep cycles to get the desired result.

Guffa
+3  A: 

Your code is executing Application.DoEvents() constantly in the while loop, for the time duration specified in your time parameter. This will consume one core of your CPU, which is why you're seeing 50% processor usage (you have a dual-core processor, correct?). This is an ugly way to wait. You could instead call Thread.Sleep(), passing it the number of milliseconds you'd like your thread to wait.

If you'd like your application to stay responsive, you might also spin off a timer, and block the UI from any action until the timer triggers. Something like (lightly tested):

// constructor or designer code
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Tick += new EventHandler(timer_Tick);

void Wait(int interval)
{
    timer.Interval = interval;
    timer.Start();
    BlockUIOperations(); // implement yourself
}

void timer_Tick(object sender, EventArgs e)
{
    timer.Stop(); 
    EnableUIOperations(); // implement yourself
}

Here's my attempt at a translation into VB:

'' Add a Timer object to the form named "Timer".
'' Hook its Tick event to Timer_Tick

Private Sub Wait(ByVal interval As Integer)
    Timer.Interval = interval
    Timer.Start()
    BlockUIOperations() '' implement yourself
End Sub

Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer.Tick
    Timer.Stop()
    EnableUIOperations() '' implement yourself
End Sub
Michael Petrotta
Thread.Sleep() doesnt update the UI, does it?
Cyclone
And yeah,dual-core processor.
Cyclone
See my code for a timer-based implementation that keeps the UI updated.
Michael Petrotta
That isn't working, it just generated no less than 10 syntax errors.
Cyclone
@Cyclone: Such as? I'm guessing some of those come from BlockUIOperations and EnableUIOperations, which you'll need to implement, of course.
Michael Petrotta
Well, I tried it inside of my sub as a substitute, it said some things were undeclared and it also "got mad" because of the semicolons.
Cyclone
All errors seemed to result from being unable to declare the timer properly...
Cyclone
My code is C#. You can translate it yourself, or try the VB I added to my answer. You should do more than just copy and paste it, though. Walk through it, understand what it does, and implement the UI operation functions if needed.
Michael Petrotta
I do not see where timer_tick should be called.
Cyclone
@Cyclone: the comment at the head of the code discusses that. Hook the Timer's Tick event to Timer_Tick, in the form designer or by hand.
Michael Petrotta
Done, i missed the Handles section of that line lol. Anyway, it did not work unfortunately, as the application started it promptly froze, yet I executed the code correctly I believe.
Cyclone
Then just read up on Timers and add the code yourself. It's a good thing to know how to do.
Michael Petrotta
A: 

If you don't mind blocking the current thread, you could use a WaitHandle.


    Public Sub Wait(ByVal ms As Integer)

        Using wh As New ManualResetEvent(False)
            wh.WaitOne(ms)
        End Using

    End Sub

    Sub Main()

        Console.WriteLine("Hello World!")
        Wait(5000)
        Console.WriteLine("Good-Bye!")

    End Sub

Of course, something more complex can be constructed depending on what you are trying to accomplish.

Sure, so long as it will resume the thread and update the UI, that is perfect.
Cyclone