Valoda
Valoda

Reputation: 335

Async timer in loop function without stopping main thread

I created WPF form with a "Start" button, this button is "StartMacros" function.

When i click "Start" button i want to run a loop and open a firefox browser every 10 seconds, without stopping the MAIN thread, so i can create another "Pause" button or "Cancel" button

Right now everything is working but there is no delay in the loop, how can i create it? the "await Task.Delay((10000));" is not creating the delay..

    public void StartMacros(object sender, RoutedEventArgs e)
    {
        AsyncTimerLoop();
    }

    public async void AsyncTimerLoop()
    {
        for (int profileNumber = 1; profileNumber <= 10; profileNumber++)
        {
            OpenBrowser(profileNumber);
            await Task.Delay((10000));
        }
    }
    public void OpenBrowser(int profileNumber)
    {
        System.Diagnostics.Process.Start("firefox.exe", "-P " + profileNumber + " -no-remote imacros://run/?m=\"" + ImacroFilePath + "\"");
    }

Upvotes: 0

Views: 1746

Answers (3)

rory.ap
rory.ap

Reputation: 35318

You can accomplish this with just one method:

private async void StartMacros(object sender, RoutedEventArgs e)
{
    for (int profileNumber = 1; profileNumber <= 10; profileNumber++)
    {
        OpenBrowser(profileNumber);
        await Task.Delay(10000);
    }
}

Note, I have added the async keyword to the method.

What's going on here is subtle. The reason your code didn't work as expected was because your StartMacros was calling your AsyncTimerLoop method synchronously. It calls the method which then loops a bunch of times really fast on the main thread. Within each loop iteration, it calls your OpenBrowser (still on the main thread) and then awaits a call to Task.Delay (which actually returns an awaitable Task, but your code doesn't do anything with that -- it just throws them away). Since you called it with the await keyword, it does that on another thread -- one thread for each loop iteration -- and just continues looping on the main thread, calling OpenBrowser in each loop, until it finishes the loop and exits the function; all this happens very rapidly. The calls to Task.Delay don't do anything except delay for ten seconds on another thread and then exit. They're basically useless.

Now, you could do this:

private async void StartMacros(object sender, RoutedEventArgs e)
{
    await AsyncTimerLoop();
}

public async Task AsyncTimerLoop()
{
    for (int profileNumber = 1; profileNumber <= 10; profileNumber++)
    {
        OpenBrowser(profileNumber);
        await Task.Delay((10000));
    }
}

Notice here that I've added the async keyword to StartMacros just like in my code above, and I'm also awaiting the call to AsyncTimerLoop. In order to do that, I had to change the return type of AsyncTimerLoop to Task, making it awaitable. Now when you run the code, it awaits the whole call to AsyncTimerLoop on a separate thread, immediately returning control to to StartMacros which exits right away so the application event loop can resume, leaving the application responsive. Meanwhile, the whole AsyncTimerLoop task is running on a separate, single thread, waiting ten seconds during each loop iteration and therefore waiting ten seconds between each call to OpenBrowser.

My code is similar to this modified version of yours, it's just more concise.

Upvotes: 2

BanksySan
BanksySan

Reputation: 28540

Wouldn't a timer be easier?

var timer = new Timer(o => Console.WriteLine("{0} Tick", DateTime.Now), null, 0, 6000);

Upvotes: 0

Arun D
Arun D

Reputation: 464

Try this code, use

 var task = Task.Run(() => OpenBrowser(profileNumber));
    if (task.Wait(TimeSpan.FromSeconds(10000)))
    {
       return task.Result;   
    }   

Add System.Threading.Tasks namespace to your code to access Task Parallel Library.

 using System.Threading.Tasks;


 public void StartMacros(object sender, RoutedEventArgs e)
 {
    AsyncTimerLoop();
 }

public async void AsyncTimerLoop()
{
    for (int profileNumber = 1; profileNumber <= 10; profileNumber++)
    {
        var task = Task.Run(() => OpenBrowser(profileNumber));
        if (task.Wait(TimeSpan.FromSeconds(10000)))
        {
          return task.Result;   
        } 
    }
}
public void OpenBrowser(int profileNumber)
{
    System.Diagnostics.Process.Start("firefox.exe", "-P " + profileNumber + " -no-remote imacros://run/?m=\"" + ImacroFilePath + "\"");
}

Upvotes: 0

Related Questions