user2102815
user2102815

Reputation: 11

How to wait between functions

I want to do the following without using Thread.sleep.Basically I have to call 3 functions with some wait time between each of them and run the whole thing over a loop for 100 times.

I tried using the Timer class,but it seems I am using it incorrectly.

   func test()
 for(int i=0;i<100;i++){
      func A()
      wait for 20 seconds
      func B()
      wait for 45 seconds
      func C()
      wait for 2 mins
}//repeat for 100 times

Upvotes: 1

Views: 385

Answers (2)

mikef
mikef

Reputation: 31

You might be looking for the Task framework. You can do something like this:

async void Run()
{
    for (int i=0; i<100; i++)
    {
        A();
        await Task.Delay(TimeSpan.FromSeconds(20));
        B();
        await Task.Delay(TimeSpan.FromSeconds(45));
        C();
        await Task.Delay(TimeSpan.FromMinutes(2));
    }
}

The function will return immediately, but that loop will keep running in the background.

Edit: As svick noted below, this is a new .NET 4.5 feature.

Upvotes: 2

Eduard Dumitru
Eduard Dumitru

Reputation: 3262

I'm gonna go and make a big assumption (all clues point in that direction):

I'm presuming you're doing all of this in Windows Forms and you would like the GUI not to freeze during the Thread.Sleep() invocations, right ? If that is the case then you could write this method:

public static class Foo {
  public static void MySleep(int milliseconds) {
    int startTick = Environment.TickCount;
    while (Environment.TickCount - startTick < milliseconds)
      Application.DoEvents();
  }
}

and then replace the Thread.Sleep invocations with Foo.MySleep invocations (but BEWARE of the implications of Application.DoEvents, which allows the entire process to be executed all over again while the control is inside of any MySleep invocations. I don't recommend doing things this way if you don't take precautions -- for instance you should always disable the button that calls func test() while test is executing because it can get executed again and again in a never ending recursion) :

func test()
  for(int i=0;i<100;i++){
    func A()
    Foo.MySleep(20 * 1000);
    func B()
    Foo.MySleep(45 * 1000);
    func C()
    Foo.MySleep(2 * 60 * 1000);
}//repeat for 100 times

There is another way (a bit safer) of doing just that. It involves a secondary thread and some Control.Invoke calls. It also allows you to use the standard System.Thread.Sleep(int milliseconds) method.

And it goes something like this:

public class Form1 : Form {

  public void triggerTestAndReturnImmediately() {
    new Thread(start: () => {

      Action callA = () => funcA();
      Action callB = () => funcB();
      Action callC = () => funcC();

      for(int i=0;i<100;i++){
        this.Invoke(callA);
        Thread.Sleep(20 * 1000);

        this.Invoke(callB);
        Thread.Sleep(45 * 1000);

        this.Invoke(callC);
        Thread.Sleep(2 * 60 * 1000);
      }//repeat for 100 times


    }).Start();
  }

}

The "this.Invoke( someDelegate )" bit is based on the fact that the method "triggerTestAndReturnImmediately" is a member of a Form subclass and "this" is a Form instance.

What goes on here is that you run the waiting operations + the triggering of actual workers (funcA, funcB, funcC) on a separate thread. Why do you run only the trigerring of the actual workers and not the actual workers themselves ? Because you're most likely going to access the GUI objects from those funcs and that is not permitted in WinForms (and many other .NET or non .NET GUI frameworks for that matter).

So you're asking the GUI thread (which as you pointed out in your question) must be free in 99% of the time to execute those functions - hence the this.Invoke ( someDelegate ) invocations.

You should take the same precautions I mentioned earlier but all that is up to you and what you want your application to do. Presuming you want to have a button's Click event handler call the triggerTestAndReturnImmediately and you want it to be disabled during the process, how would you go about disabling and reenabling the button ?

If you do this:

void buttonBar_Click(object sender, EventArgs e) {
  this.buttonBar.Enabled = false;
  this.triggerTestAndReturnImmediately();
  this.buttonBar.Enabled = true;
}

then that wouldn't do you any good, because the triggerTestAndReturnImmediately method does just what it's name implies: "it returns immediately".

So how would you pull this off ? You could do something like:

void buttonBar_Click(object sender, EventArgs e) {
  this.triggerTestAndReturnImmediately();
}


  public void triggerTestAndReturnImmediately() {
    this.buttonBar.Enabled = false;

    new Thread(start: () => {

      Action callA = () => funcA();
      Action callB = () => funcB();
      Action callC = () => funcC();
      Action reenableButton = () => this.buttonBar.Enabled = true;

      for(int i=0;i<100;i++){
        this.Invoke(callA);
        Thread.Sleep(20 * 1000);

        this.Invoke(callB);
        Thread.Sleep(45 * 1000);

        this.Invoke(callC);
        Thread.Sleep(2 * 60 * 1000);

        this.Invoke(reenableButton);
      }//repeat for 100 times


    }).Start();
  }

Also make sure you always reenable the button whether the invocation sequence crashes or not with some try finally statement or other gymnastics.

So I think you can get an idea of what the precautions I was talking about are (they depend on what you want your app to do).

Upvotes: 0

Related Questions