CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Patrick Smacchia [MVP C#]


BackgroundWorker Closure and Overridable Task

 Clearly, the class System.ComponentModel.BackgroundWorker is a good friend for all Windows Form programmers. Basically, it lets developers delegate a computation intensive task to a non-UI thread in order to get a responsive UI. The BackgroundWorker class lets specify a callback procedure through the event RunWorkerCompleted. The real benefit of the BackgroundWorker class is that this callback procedure is ran by the UI thread once the task done.

 
The BackgroundWorker class also addresses 2 useful scenario: task cancellation and progression report callback on the UI thread.

 

The Overridable Task Scenario 

I found an important scenario that is not addressed by the BackgroundWorker class: overridable task. What I want is to trigger a task and be able to trigger again the same task without waiting for the first occurrence to complete. The first task must be cancelled before the second one being run since a BackgroundWorker object cannot execute 2 tasks at the same time.

 

I found this scenario useful because it lets refresh a sophisticated UI in real-time on intensive user input, without degrading UI responsiveness. Typically, this happens when you’re implementing some kind of intelligence on mouse move when a non-trivial computation is triggered each time the MouseMove event is raised (typically several dozen times a second). This happens also on some text typing facility such as intellisense, where the user can type several characters a second and the computation might take a second or even more to be computed. In both cases, the UI must remain responsive and the computation has to be re-trigged, each time a new character is inputted or each time the MouseMove event provides new mouse coordinates.

 

Here is a concrete use of the overridable task pattern in the NDepend UI. When editing a CQL query, both the list of code elements matched by the query (the Query Result panel), and the treemap where matched code elements are painted in blue (the Metric panel) gets updated in real time. The screenshot below shows that the intellisense lets the user modify the threshold on the query: SELECT METHODS WHERE NbParameters > {Threshold}. On large code base with hundreds of thousands of methods, the UI must remain responsive when the user moves the cursor of the intellisense to update the threshold. As a consequence, we needed the overridable task pattern to update matched methods in the Query Result and the Metric panel.

 

 

 

Implementation of the Overridable Task scenario with BackgroundWorker  timer and closure

At first glance, the BackgroundWorker comes with 2 convenient members: CancelAsync() and IsBusy(). However, the overridable task can’t be implemented naively by just calling CancelAsync() and then calling IsBusy() in an infinite loop containing a Thread.Sleep( a few microseconds here ).

  • First, this might kill responsiveness since the UI thread is blocked while waiting for the first task to be cancelled.
  • Second, calling Thread.Sleep(...) in an infinite loop is a typical anti-pattern that wastes thread, a proper timer must always be used in such circumstances.
  • And third, even worth, this just doesn’t work: The IsBusy() method will never return false! I am not sure about the implementation of BackgroundWorker but it seems that the internal isBusy state must be reset internally from the UI thread. Thus, you need to release the UI thread and then re-enter one of your procedure before observing IsBusy() returning false.

 

The only right approach here is to create a System.Window.Forms.Timer object triggered just after calling CancelAsync(). The timer will tick until the method IsBusy() returns false, and then it’ll be time to push the new task. So basically one needs to create a timer field + a timer callback procedure + a state field to store the input data of the new task to trigger. This sort of situation is well handled by closures. Here is the method BeginWork() code with a closure that avoids to create extra fields and extra procedures:

 

 
Notice that I use here a C#2 anonymous method to implement the closure. A lambda expression can be used also, but in this particular case it is less concise since the 2 tick procedure parameters (sender and arg) would need to be specified.

 

A bug in the implementation

While this first implementation seems to me correct at first sight, it contains a subtle bug which effects appear only on certain condition. Imagine that the currently running task takes time to be cancelled. To make things concrete, I provide here a prototype where the task can be cancelled every 500ms only and where the task takes 3s to be computed. What happens if I trigger several time a second the task by clicking quickly the Go! Button? The following screenshot shows that actually, several timers gets created and they are competing to make their tasks run. And we can see that the task#7, the last one triggered, is actually not the one that ends up being executed thoroughly.

 

Correcting the bug

There is no way to fix this bug without adding at least one instance field. Indeed, the fact that a new task is waiting to be ran must be stored across each call to BeginWork(). This storage cannot be done with a captured state by a closure because it could then not be read from further calls to BeginWork(). Let’s add m_Argument field. If it is not null, it means that there is a task pending to be ran. Thus a second timer cannot be triggered, but the input argument to the task needs to be updated with the new input.

 

 

Nothing is obvious with asynchronous programming. Take the time to understand why  the scope (m_Argument != null)  comes before the scope (!m_BackGroundWorker.IsBusy). This lets handle the scenario where a background timer is still waiting for the current task to end up (because it has been cancelled)  and  between 2 ticks, a new task is posted.  Thus m_Argument is updated, and the newer task will be executed as soon as the timer can start it on the BackgroundWorker.


At first sight, accesses to m_Argument should be synchronized because the field can be written both by entering BeginWork(...) or by a timer tick. One elegant aspect is that these accesses are always done by the same thread, the UI thread. In other words the field m_Argument has an affinity with the UI thread. As a consequence, there is no need for synchronization.

And here is a screenshot of the prototype in action:

 




Comments

Dew Drop – June 11, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop – June 11, 2008 | Alvin Ashcraft's Morning Dew

# June 11, 2008 8:35 AM

Hüseyin Tüfekçilerli said:

Great article! I will probably need to use this pattern in the future. Btw the link to sample project seems to be broken.

# June 12, 2008 3:42 AM

Patrick Smacchia said:

Indeed, the link was broken, it is now working, thanks

# June 12, 2008 5:09 AM

Duncan said:

Thank you SO much.

Been struggling with this for ages.

# June 24, 2008 11:50 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Patrick Smacchia

Patrick Smacchia is a Visual C# MVP involved in software development for over 15 years. After graduating in mathematics and computer science, he has worked on software in a variety of fields including stock exchange, airline ticket reservation system as well as a satellite base station at Alcatel. He's currently a software consultant and trainer on .NET technologies as well as the lead developer of the tool NDepend which provides numerous metrics and caveats on any compiled .NET application. He is the author of Practical .NET2 and C#2, a .NET book conceived from real world experience with 647 compilable code listings. Check out Devlicio.us!

Our Sponsors

Free Tech Publications