Creating an asynchronous timer

Here’s some interesting code I thought I might share. It’s an asynchronous timer, and allows you, in Windows Forms, to have a simple timer where you specify an interval, a condition, some code to execute on the interval, and optionally some other code to execute after the timer condition evaluates to false. One thing to be careful of though, is I am not following best practices here: This is an async void method, which polls.

Again, the source, for this and a whole bunch of other stuff, is here: RomyView.zip

  1. /// <summary>Starts an asynchronous polling timer, similar to a thread timer.</summary>
  2. /// <param name=”condition”>The condition which indicates that polling should continue.</param>
  3. /// <param name=”pollInterval”>Similar to a timer tick, this determines the polling frequency.</param>
  4. /// <param name=”pollingAction”>The action to be executed after each poll interval, as long as the condition
  5. /// evaluates to true. This works like a timer event handler.</param>
  6. /// <param name=”afterAction”>An optional action to be executed after the condition evaluates to false.</param>
  7. protected async void CreatePollingTimerAsync(Func<bool> condition, TimeSpan pollInterval, Action pollingAction, Action afterAction = null)
  8. {
  9.     while (condition())
  10.     {
  11.         await Task.Delay(pollInterval);
  12.  
  13.         if (condition())
  14.             pollingAction();
  15.     }
  16.  
  17.     if (afterAction != null)
  18.         afterAction();
  19. }

 

And some examples of using it…

This is the simplest one. Called from my video player, it ensures that while the form is visible, the system will not enter Sleep mode. (PowerManagement.DisplayIsInUse() just calls the WindowsAPI SetThreadExecutionState method. I’ll include that here as well, for clarity.)

  1. protected void IndicateDisplayInUse()
  2. {
  3.     CreatePollingTimerAsync(() => !IsDisposed && Visible, TimeSpan.FromMinutes(1), () => PowerManagement.DisplayIsInUse());
  4. }

 

Here is the PowerManagement code:

  1. using System.Runtime.InteropServices;
  2.  
  3. namespace System
  4. {
  5.     /// <summary>Notifies the system that the display is in use. To be used, for example,
  6.     /// when playing video, since the system is unable to detect video activity alone and
  7.     /// will incorrectly detect that it is idle, and enter sleep mode.</summary>
  8.     public static class PowerManagement
  9.     {
  10.         /// <summary>Informs the system that the display is in use.</summary>
  11.         public static void DisplayIsInUse()
  12.         {
  13.             NativeMethods.SetThreadExecutionState(ExecutionStates.DisplayRequired);
  14.         }
  15.  
  16.         internal static class NativeMethods
  17.         {
  18.             [DllImport(“kernel32.dll”, SetLastError = true)]
  19.             public static extern ExecutionStates SetThreadExecutionState(ExecutionStates esFlags);
  20.         }
  21.  
  22.         [Flags]
  23.         internal enum ExecutionStates
  24.         {
  25.             /// <summary>No state configured.</summary>
  26.             None = 0,
  27.  
  28.             /// <summary>Forces the system to be in the working state by resetting the system idle timer.</summary>
  29.             SystemRequired = 0x1,
  30.  
  31.             /// <summary>Forces the display to be on by resetting the display idle timer.</summary>
  32.             DisplayRequired = 0x2,
  33.  
  34.             /// <summary>Enables away mode. This value must be specified with ExecutionStates.Continuous.
  35.             /// Windows Server 2003 and Windows XP/2000: ExecutionStates.AwayModeRequired is not supported.</summary>
  36.             AwayModeRequired = 0x40,
  37.  
  38.             /// <summary>Informs the system that the state being set should remain in effect until the next
  39.             /// call that uses ExecutionStates.Continuous and one of the other state flags is cleared.</summary>
  40.             Continuous = unchecked((int)0x80000000)
  41.         }
  42.     }
  43. }

 

And here is another example of using the timer. I won’t copy all the related code here though… It is used in my video player, and all it does is, as long as the video is still playing (and hence we are seeking), it updates the seeking slider.

One must be careful with the first parameter to this method. Below, the first parameter being passed is a lambda expression that will cause my video player’s Seeking property to be evaluated each time. Passing a boolean value here, instead of a lambda expression or delegate, would be a mistake, because that would cause the condition to always evaluate to whatever that boolean value passed initially is, which is of course not the intention.

  1. /// <summary>An async timer to update the slider.</summary>
  2. private void Seek()
  3. {
  4.     CreatePollingTimerAsync(() => Seeking, TimeSpan.FromMilliseconds(100), () => UpdateSeeker());
  5. }

 

Enjoy!

Update: What is the significance of this being an async void method that polls?

It depends… Mine is being called from a Form. In that case, depending on the length of the interval duration, the timer might just tick after the Form is already disposed. So the code needs to handle that. It’s not a big deal in my video player, because the form is hidden and not closed after playing video. But notice in my first example, I added a check for the form being disposed before checking its Visible property, to avoid a potential exception if the timer ticks while the application is being closed.

Advertisements

About Jerome

I am a senior C# developer in Johannesburg, South Africa. I am also a recovering addict, who spent nearly eight years using methamphetamine. I write on my recovery blog about my lessons learned and sometimes give advice to others who have made similar mistakes, often from my viewpoint as an atheist, and I also write some C# programming articles on my programming blog.
This entry was posted in Programming and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s