Aasas
Aasas

Reputation: 51

How to pause loop while time is ticking

I have made a timer where I can set time I want to wait and then do something. So this is my short timer func:

private void Sleep(int interval, Action action)
{
    System.Windows.Forms.Timer mytimer = new System.Windows.Forms.Timer();
    mytimer.Interval = interval; //interval is in ms   
    mytimer.Start();
    mytimer.Tick += (s, e) =>
    {
        action();
        mytimer.Stop();
    };
}

And Im using this timer in loop:

foreach (string word in words)
{
   Sleep(5000, ()=> myAction());                                           
}

Without loop timer is great but in loop it wont work because loop wont stop and wait for those 5secs. It do all stuff imediately and starts timer again and again too fast.

So what Im trying to find out is how to make my loop wait until time runs out and myAction() is executed. Im working on forms application so all threadin sleeps wont work here. Also I tried all other timers but they used too much CPU.

Upvotes: 0

Views: 1226

Answers (3)

Vignesh.N
Vignesh.N

Reputation: 2666

you are better for adding all actions to a queue and then executing once the timer is elapsed.
Have only one time currently your code suggests you are creating a new timer each time the Sleep method is called.
Also the timer you are using runs on the main dispatcher of the current Window,
may be you want to use a DispatcherTimer instead if you want to specify and run the timer on a different thread.

private Queue<Tuple<string,Action>> _queuedAction = new Queue<Tuple<string,Action>>();

then

foreach(var word in words){
 _queuedAction.Enqueue(new Tuple<string, Action>(word, ()=>{});
}
var timer= new System.Windows.Forms.Timer();
timer.Interval = interval;
timer.Elapsed += OnTimerElapsed;
timer.Start();

then

private void OnTimerElapsed(object sender, EventArgs e){
 if(!_queuedAction.Any()) return;
 var tuple = _queuedAction.Dequeue();
 //Execute your Action here.
 var word = tuple.Item1;
 var action = tuple.Item2;
 action();
}

here we are adding actions to a queue and every time the interval is elapsed the OnTimeElapsed method will get called then inside it we execute the action.

Else if you are looking for a synchronous solution we could simple have something like this

foreach(var word in words){
 DoSomethingWithWord(word);
 Thread.Sleep(5000);
}

Upvotes: 1

rory.ap
rory.ap

Reputation: 35318

The reason it "seems" like it's happening again immediately is because your foreach loop is actually happening without any delays -- at the speed your processor can loop -- and calling Sleep many, many times right away, thus queuing up all those timers which are each waiting your 5000 milliseconds, then all firing your action after that.

You could do this instead of your foreach loop and get rid of all the other code (i.e. the Sleep method):

System.Threading.Tasks.Task t = System.Threading.Tasks.Task.Factory.StartNew(() =>
    {
        var timer = new System.Threading.Timer(new System.Threading.TimerCallback(x => this.Invoke(myAction)), null, 5000, 5000);
    });

Note a few things here:

  • The UI thread is not blocked by the timer because it's being run on a separate thread, whereas your code would block the UI as it was looping.

  • The timer class being used is a System.Threading.Timer, not a System.Windows.Forms.Timer (see this for a good explanation of the .NET timers)

  • If you want to perform any operations on the UI thread from within the timer (i.e. within your myAction delegate) you must do so using this.Invoke(myAction) which is one of the important rules of multithreaded operations involving the UI thread.

Upvotes: 0

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149628

Your loop doesn't stop because the timer doesn't actually execute any command which halts the current threads execution.

Though your example is a bit vague, i am assuming what you're looking for isTask.Delay which internally uses a timer. You can asynchronously wait it so the loop yields control back to the caller:

foreach (string word in words)
{
    await Task.Delay(5000);
    action();
}

Note this means you'll have to mark your method as async Task, or if its an event handler async void.

Upvotes: 0

Related Questions