Developed a basic, light-weight library for scheduling tasks in C# .Net

Source code can be found at GitHub

TaskSchedular

TaskScheduler is a simple and efficient C# .Net library that runs given tasks at the specified date and time.

  • Efficient : There is no polling. Only runs when a task is due. This is achieved though AutoResetEvent.
  • Simple : Merely 8 KB in size. Pretty easy to use but addresses limited number of use cases.

Background

.Net Framework comes with various Timer classes which give us the ability to run a task periodically.

Apart from Timers which run periodically, we don’t have any class which executes tasks at a given time. A usual work around is to use Timer of a second or so (based on your need), then keep checking if any task is due in timer’s event and execute the task when due. In order to be more resource friendly, TaskSchedular takes a different approach instead of continuous polling for task due date.

How TaskScheduler Works

It runs in it’s own thread and doesn’t consume any CPU resouces till a task is due. This is acheived through Wait Handle (AutoResetEvent). All scheduled tasks are executed in the same thread, which means:

  • tasks are not running in GUI thread so any code which is related to GUI should be invoked on the GUI thread. See this blog post for running code in GUI thread using Control.Invoke.
  • tasks are never executed in parallel as there is only one thread. This has following upshots:
    • saves us from thread synhronization issues within tasks.
    • this library might not the the right one for you if you need more parallelism. In such a case, check out alternatives like Quartz.NET and FluentScheduler.

Usage

Starting TaskScheduler
var schedular = new TaskSchedular.TaskSchedular();
schedular.Start();
Adding task
schedular.AddTask(new TaskSchedular.Task()
    {
        StartTime = DateTime.Now.AddSeconds(30),
        TaskAction = () =>
        {
            // do some work here
            System.Threading.Thread.Sleep(300);
        },
        Recurrance = TimeSpan.FromSeconds(30)
    });

Note: TaskSchedular is has a tolerance of 1 second by default, that is, if a task is due within a second, it will execute it right away.

Book Review: Threading in C# by Joseph Albahari

Threading is a complex subject, it is easy to get yourself entangled in threads which don’t behave as you expect them to. There are many constructs and approaches to synchronize threads or achieve parallelism, each with its own quirks and advantages. There are good old threads, there are thread pools, task parallelism, Parallel class, Async methods and Timers. Then there are a myriad ways of synchronizing thread like locks, spin locks, mutex, semaphores, event wait handles, concurrent collections, Synchronization attribute, Memory Barriers, Interlocked class, Monitor’s Wait/Pulse and Reader Writer Lock . To make sense of it all,  Threading in C# by Joseph Albahari is thorough reference on the subject. It is available online, free of charge in hypertext format which makes it particularly useful as a reference.

csianThreading in C# is also there as a chapter in somewhat abridged form in author’s book ‘C# in a Nutshell‘.

 

Async / Await and SynchronicationContext (C# .Net)

Following innocuous looking async / await code will cause deadlock.

private void button1_Click(object sender, EventArgs e)
{
  Task<string> task1 = LongRunningProcess();

  textBox1.Text = task1.Result;
}

public async Task<string> LongRunningProcess()
{
  string txt = await Task.Run(() =>
    {
      System.Threading.Thread.Sleep(5000);
      return "results";
    }
  );

  return txt;
}

To understand why, lets go into what await does in the above case.

1 Execution starts when the button is clicked and button1_Click event is fired on the UI thread.
2 The method, named LongRunningProcess, is invoked.
3 The lamda expression passed to Task.Run() executes in a separate thread (lets call it thread_B)
4 Now await keyword is encountered, rather than completing the rest of the method, control returns back to button1_Click event to continue execution after the call to long running method.
5 Calling task1.Result makes the current thread wait on thread_B to complete and provide the results. So UI thread is now waiting for thread_B to complete.
6 As thread_B completes the task, it has to run the remaining part of long running method. Run time ensures that this code executes on the right context. That is, if the initial part of long running method was executed on the UI thread, then remaining part will also be executed in the same thread context.
7 Therefore, thread_B now attempts to run the remaining part of long running method on UI thread, while UI thread is waiting for thread_B to finish.
8 As UI thread and thread_B are waiting for each other, this creates the deadlock.
How to avoid deadlock?

In this case, deadlock can be avoided by anyone of the following ways:

1- Use await keyword while calling long running method. This makes the button click event asynchronous also.

private async void button1_Click(object sender, EventArgs e)
{
  textBox1.Text = await LongRunningProcess();
}

2- Call ConfigureAwait(false), this will inform the run time that the remaining part of long running method doesn’t need to execute on the UI thread, it can continue running on the thread pool.

public async Task<string> LongRunningProcess()
{
  string txt = await Task.Run(() =>
    {
      System.Threading.Thread.Sleep(5000);
      return "results";
    }
  ).ConfigureAwait(false); //avoids the deadlock

  return txt;
}
Why thread contexts are synchronized?

Consider the UI Controls (Windows Forms or WPF), they are not thread safe. Therefore, any update to the UI controls must be done only from the UI thread. To take care of this, any remaining code after await keyword in an asynchronous method will also execute on the thread context which initiated the method call.

Similarly, in an ASP.Net application, running the code on same thread context is important because the Culture, Principal and other information of the request are stored in the thread.

How thread contexts are synchronized?

To manage all this tricky context synchronization, we have SynchronizationContext class in .Net. There are framework specific implementations (derived classes) for Windows Forms, WPF/Silverlight and ASP.Net which handle the SynchronizationContext in their own ways for the framework to function properly.

The Windows Forms implementation uses Control.Invoke method to accomplish this (more details here). For ASP.NET, execution takes place on a different thread but the context is captured and passed on to the new thread.

There is an excellent article on MSDN regarding the SynchronizationContext which I recommend for details.

 

Creating and Consuming Async methods (C# .Net)

The code we want to run asynchronously could either be CPU-bound or IO-bound. CPU-bound code keeps the CPU busy, requiring intensive processing, calculations etc. On the other hand, IO-bound code frees up the CPU while waiting for an IO operation to complete, for instance, get some data from a web service. Both kinds of asynchronous methods are illustrated below.

 

 Creating Asynchronous method

 

CPU-bound Async method
public async Task<string> OurMethodAsync()
{
  string x = await Task.Run(() =>
    {
      // performs CPU intensive work
      return LongRunningTask();
    });

  return x;
}

Above method doesn’t do anything special. Rather than performing the work itself, it creates a thread and delegates the work to it. It will not make the task run faster, in fact, it may take more time due to multi-threading overhead. One reason to do things this way is to free the calling thread and not keep it busy for long time. This is required in case of User Interface threads, for instance.

 

IO-Bound Async method

For IO operation such as working with file system, requests to web servers etc., .Net framework already provides us with methods which run asynchronously. These methods uses lower levels OS calls to provide asynchronous behavior for blocking IO operations.

Below is our asynchronous method which uses .Net Framework’s WebClient.DownloadStringTaskAsync method.

public async Task<string> GetWebPage(string url)
{
  var webClient = new WebClient();
  string txt = await webClient.DownloadStringTaskAsync(url);

  return txt;
}

 

 Consuming asynchronous method

 

Consuming asynchronous method with await
private async void button1_Click(object sender, EventArgs e)
{
  textBox1.Text = await GetWebPage("http://www.yahoo.com");
}

 

This button click event consumes the IO-bound async method defined above. If GetWebPage method is taking a long time, the control will return back to the caller method. Once results of GetWebPage are available, execution will start again with the instruction after await keyword. This will ensure that the event does not block the UI thread.

Calling asynchronous methods concurrently

We can also use asynchronous method to run tasks in parallel.

private async void button1_Click(object sender, EventArgs e)
{
  Task<string> task1 = GetWebPage("http://www.yahoo.com");
  Task<string> task2 = GetWebPage("http://www.google.com");

  await Task.WhenAll(task1, task2);

  textBox1.Text = task1.Result;
  textBox2.Text = task2.Result;
}

 

Notice we are not using await keyword now when invoking GetWebPage method. This causes the return type to change also, we are expecting Task rather than string object.

The flow control is also very different. If GetWebPage is blocking, the control doesn’t return to the caller method, rather execution continues to the next statement in the event.

We have await in the third line which will relinquish control to the caller method if task1 or task2 are not completed yet. When the result of the two tasks are available the last two lines of code are executed on the UI thread.

Keeping UI Responsive (C# .Net)

The User Interface (UI) becomes unresponsive when a time taking operation is executed during an event.

Consider the following button click event where I have simulated a time taking operation by calling Sleep method. In a real scenario, it could be some processing which produces the result to be shown back on the user interface.

private void button1_Click(object sender, EventArgs e)
{
   textBox1.Text = "Calculating result…";
   // long running operation simulated through Thread.Sleep
   System.Threading.Thread.Sleep(5000);

   textBox2.Text = "Final result";
}

 

Keeping UI responsive through System.Threading.Tasks.Task

To keep the UI responsive, we can spawn a new thread to perform the time taking operation. Earlier, we used to do it with System.Threading.Thread but now we have more convenient Task class from TPL (Task Parallel Library) in .Net.

private void button1_Click(object sender, EventArgs e)
{
   // do initial UI update
   textBox1.Text = "Calculating result…";

   // spawn a new thread for the long running operation and final UI update
   Task.Run(() =>
      {
         System.Threading.Thread.Sleep(5000);
         // unsafe call to UI control
         textBox2.Text = "Final result";
      });
}

 

If we run the above code in debug mode, we get the following exception:

image

UI controls are not thread-safe, therefore, call to the UI should only be made from UI thread which created these controls.

The way around this problem is to use Invoke method of the control and pass it a delegate which performs the required UI changes. Invoke method will ensure that the delegate is executed on the UI thread.

private void button1_Click(object sender, EventArgs e)
{
   textBox1.Text = "Calculating result…";

   Task.Run(() =>
   {
      System.Threading.Thread.Sleep(5000);

      // using Control.Invoke
      Action act = () => textBox2.Text = "Final result";
      textBox2.Invoke(act);
   });
}

This will keep the UI responsive in a thread safe way.

 

Keeping UI responsive using async / await

Another way of doing this is through async / await keywords.

private async void button1_Click(object sender, EventArgs e)
{
   textBox1.Text = "Started";

   await Task.Run(() => System.Threading.Thread.Sleep(5000));

   textBox2.Text = "Final result";
}

 

This code will achieve the same results as above.

With async / await, the code looks more synchronous and readable. As await keyword is encountered, a new thread is started for processing the task and the method returns the control back to the caller (this ensures that the UI thread is free and responsive). Once the task is completed, rest of the code for the click event is executed on the UI thread. Behind the scene, compiler generates complex state machine logic to make this happen seamlessly.