(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Threading recipes: the many ways of spawning background works in Silverlight

(5 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
2 comments   /   posted on May 18, 2011
Categories:   General

People often think at Silverlight like a beautiful toolset useful to create amazing interfaces on the web. Since it is true for sure that it does not exists a similar toolset that join power and simplicity like Silverlight does, it's already true that under the cover of the user interface it beats a tiger heart that is capable of features that are typically reserved to desktop frameworks. As an example, a respectful application framework can't play its role without the capability of processing background operations and from this point of view Silverlight is not second to any other.

Processing background operations is important for a number of reasons; first of all, since we are developing application that have an user interface it is important to process things without locking the UI. When you call the network this is implicitly done but there is many cases you have to do this by hand for your particular purposes. Secondary it is important to be able to optimize the computation when you have an huge number of elements process. From this point of view the threading helps you to improve the performances of the application balancing the work across multiple threads. Finally there is many times when you need to run scheduled operations - for instance when you need to poll the network - and being able to spawn a thread responsible of scheduling the activities is important to better organize the application.

Silverlight contains an huge set of threading tools and sometimes it is difficult to choose from one or the other, and also often at the way you chosen for your purpose, it corresponds another way that is better tailored for your needs.

Concurrency and Marshaling: the two edges of the threading blade

Before starting to explain some of the ways of spawning a background thread, is it required to spend some words about what you have to be strongly aware when you start writing in a multithreaded way. As you will see there is lot of opportunities for threading but the very difficult thing is not starting the background operation but usually dealing with concurrency and marshaling.

Concurrency is a concept so hard to deal with almost as simple it is to be explained. Imagine you have a resource shared by two persons that may access and change it, you need to be careful that the manipulation made by one of the two actors does not have impact with the work that is done by the other side. Explaining concurrency properly is out of the scope of this article since it probably will take lot of pages. Suffice is to understand that Silverlight has a number of tools that helps you to deal with concurrency. Lock, Monitor, Interlocked, ManualResetEvent, AutoResetEvent and WaitHandles are all types that in a way or in another can be used to manage access to shared resources. Someone is specifically drafted for the purpose of synchronizing threads (Lock, Monitor and Interlocked) and other are supporting tools that enable waiting across threads (events and WaitHandles). I suggest you to spent some time trying to understand how they works just a minute after the end of this reading and for sure before start writing a single line of code.

Marshaling is a concept strictly connected with concurrency. The problem is that like every other framework that can create user interfaces, Silverlight has a main thread running that owns every element of the user interface. This thread, called the UI Thread, is the one where our code runs. As an experiment if you try to make a Thread.Sleep(5000) answering to the click of a button, you will observe that all the user interface freezes itself because you are blocking the UI Thread, so nothing can happen during these 5 seconds. This opens to another problem: when you are in a separate thread and try to access to an UI element, to read or update it, you have a concurrency problem because the UI element is owned by the UI Thread and accessing it is simply forbidden and will raise and exception stating the cross-thread operation. For this purpose you need to marshal the context of your code to the UI Thread when you have to update the user interface and this means using an instance of the Dispatcher class that is able to do this operation. Every UI element exposes an instance of the Dispatcher but you can also use the Deployment.Current.Dispatcher property to access it and call the BeginInvoke operation that call a method you specify in the scope of the UI thread.

// given that tb is a TextBlock you can do:
tb.Dispatcher.BeginInvoke(() => tb.Text = "Hallo UI!");
 
// or simply you can use the Deployment class
Deployment.Current.Dispatcher.BeginInvoke(() => tb.Text = "Hallo UI!");

#1 Using raw Thread and ThreadStart

The first recipe in the book must necessarily be the basic way of spawning a thread. In the next parts you will find other ways that are much more refined but if your intention is to have the full control on what is happening then you have to rely on a couple of classes that let you start an operation in a raw way. These classes are Thread and ThreadStart (and its parameterized flavor ParametrizedThreadStart). To start a thread you have to create an instance if the ThreadStart class that own an handle to the method that will be the body of the asynchronous operation, and then create the Thread passing to it the ThreadStart instance. Finally you can call Start and the thread will be spawned:

void StartThreadButton_Click(object sender, RoutedEventArgs e)
{
    Debug.WriteLine("The UI thread is: {0}", Thread.CurrentThread.ManagedThreadId);
 
    ParameterizedThreadStart ts = new ParameterizedThreadStart(ThreadProc);
    Thread thread = new Thread(ts);
    thread.Start("Hallo Thread {0}!");
}
 
private void ThreadProc(object parameter)
{
    // this method is the body of the thread
    string format = parameter as string;
    Debug.WriteLine(format, Thread.CurrentThread.ManagedThreadId);
}

In this example I used the Thread.CurrentThread.ManagedThreadId property to make evident that the code is running in a separate thread. The ManagedThreadId is an integer value that is different for each thread you spawn. In this example the code running in the ThreadProc is completely asynchronous to the UI thread so here you pay the full price to concurrency and marshaling. Every time you access a shared resource you have to use a synchronization object. When you exit the body of the method the thread will exit itself (and it is evident if you look at the output window of Visual Studio while debugging) but pay attention that if you need to have a background thread that is active for the entire duration of the application (for example a scheduler) you have also to deal with lifetime of the thread and provide code to gracefully exit when the application ends. Here is an example:

private ManualResetEvent ExitEvent = new ManualResetEvent(false);
 
void StartThreadButton_Click(object sender, RoutedEventArgs e)
{
   ThreadStart ts = new ThreadStart(ThreadProc);
   Thread thread = new Thread(ts);
 
   this.ExitEvent.Reset();
   thread.Start();
}
 
void EndThreadButton_Click(object sender, RoutedEventArgs e)
{
   this.ExitEvent.Set();
}
 
private void ThreadProc()
{
   OutputTextBlock.Dispatcher.BeginInvoke(() => OutputTextBlock.Text = "Thread started");
 
   int count = 0;
 
   while(!this.ExitEvent.WaitOne(1000))
   {
       count++;
       string message = string.Format("Seconds from start: {0}", count);
       OutputTextBlock.Dispatcher.BeginInvoke(() => OutputTextBlock.Text = message);
   }
 
   OutputTextBlock.Dispatcher.BeginInvoke(() => OutputTextBlock.Text = "Thread exited");
}

#2 Using pooled threads

Many times you do not need to spawn a single thread but you have to create a number of threads to spread the computation across parallel timelines. When you are in this scenario creating threads with the previous example may hurt the performances of the application in a way that may be worst of the computation time required in a single thread scenario. This is because the operation of creating and starting a thread is not priceless but may impact the performances very heavy. The solution in this case is the use of a thread pool that is a predefined number of thread running that will execute a portion of code and then remain asleep until the next computation is requested. So you pay the price for starting a thread only the first time.

Implementing a thread pool by hand is something really hard that involve concurrency, and cross thread communications but in the Silverlight toolset you have a shining ThreadPool class that can be used to enqueue tasks to be executed in a pooled way. Let imagine we have to compute a fractal and we have a strict loop that cycle all the pixels of the target area:

void StartPoolButton_Click(object sender, RoutedEventArgs e)
{
   for (int x = 0; x < 800; x++)
   {
       for (int y = 0; y < 600; y++)
       {
           ThreadPool.QueueUserWorkItem(EvaluatePoint, new Point(x, y));
       }
   }
}
 
private void EvaluatePoint(object state)
{
   Point point = (Point)state;
 
   int iterationCount = Iterate(point);
 
   if (IsInSet(iterationCount))
   {
       Color color = this.GetColorFromIterationCount(iterationCount);
       Deployment.Current.Dispatcher.BeginInvoke(() => this.DrawPoint(point, color));
   }
}

As you can see you still need to deal with marshaling and concurrency but the operation of spawning the thread is completely managed by the ThreadPool class that accept the computation requests (QueueUserWorkItem) and enqueue them if there is not a thread free for processing a point. It is important to remember that the thread started with the ThreadPool can't be a blocking operation like the one I showed in the example #1 because the ThreadPool is tailored for multiple operations that can take a long time but that sooner or later it have to exit leaving space to other.

#3 Using the BackgroundWorker

Let say you have to run a long running operation, no matter what the operation does, you are in the case of starting a thread because if you do all computation in the UI Thread the application becomes unresponsive and this is a problem with the user that may misunderstand what is happening and believe (actually with a degree of reason) that the program has died.

Instead of using a Thread as in the example #1 you can rely on BackgroundWorker, a class that is present in the .NET Framework since the release 2.0. The benefit of the BackgroundWorker is that you can actually forget of being in a separate thread, since the class completely handle the marshaling issues, basing its work on delegates. For the BackgroundWorker to work you have to create an instance and start the operation with RunWorkerAsync. Then in the event DoWork you perform the activity. This is exactly the code that runs in background and you have to be careful of not accessing UI from inside of it. You have to return a result of the computation using the Result property of the DoWork EventArgs. The Result will be received by a RunWorkerCompleted event that is executed in the UI thread.

In addition you can also support progress reporting using the ReportProgress method and cancellation. This means you can in every moment grant the capability of cancelling the running operation and the background thread will be notified it have to stop before the end. In the sample code I simulate the supercomputer from the "The Hitchhiker's Guide to the Galaxy" that due to the heavy computation required to retrieve the ultimate answer to the universe use the BackgroundWorker to do its work an leave the user free of cancelling the request.

public partial class MainPage : UserControl
{
    private BackgroundWorker Worker;
 
    public MainPage()
    {
        InitializeComponent();
 
        this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        this.EndButton.Click += new RoutedEventHandler(EndButton_Click);
        this.StartButton.Click += new RoutedEventHandler(StartButton_Click);
    }
 
    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        this.Worker = new BackgroundWorker();
        this.Worker.WorkerReportsProgress = true;
        this.Worker.WorkerSupportsCancellation = true;
        this.Worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
        this.Worker.ProgressChanged += new ProgressChangedEventHandler(Worker_ProgressChanged);
        this.Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
    }
 
    private void StartButton_Click(object sender, RoutedEventArgs e)
    {
        this.Worker.RunWorkerAsync(DateTime.Now.Millisecond);
    }
 
    private void EndButton_Click(object sender, RoutedEventArgs e)
    {
        this.Worker.CancelAsync();
    }
 
    private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        OutputTextBlock.Text = string.Format("Examination in progress: {0}%", e.ProgressPercentage);
    }
 
    private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
            OutputTextBlock.Text = "Cancelled";
        else
            OutputTextBlock.Text = string.Format("The Answer to the Ultimate Question of Life, the Universe, and Everything is: {0}", e.Result);
    }
 
    private void Worker_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 0; i < 100; i++)
        {
            this.Worker.ReportProgress(i);
            
            if (this.Worker.CancellationPending)
            {
                e.Cancel = true;
                break;
            }
 
            if (this.CheckIfTheNumberIsTheAnswer(i))
                e.Result = i;
        }
    }
 
    private bool CheckIfTheNumberIsTheAnswer(int i)
    {
        Thread.Sleep(1000);
        return i == 42;
    }
}

 

If you run the code and start the work you will see a counter that give you the progress of the computation in percentage values. The interface remain responsive so you can hit cancel to stop the worker or wait for about 1 minute and 40 seconds to get the "Answer to the Ultimate Question of Life, the Universe, and Everything", that as everyone know is 42.

#4 Using a Timer

When you have to execute a time based operation, the best way is using a Timer. A Timer is finally a thread that is automatically spawned and every given timeout it raises an event. The best class in this case is the DispatcherTimer that is able to raise the Tick event in the UI Thread so you do not have to worry about the marshaling issues.

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    this.Timer = new DispatcherTimer();
    this.Timer.Interval = TimeSpan.FromSeconds(5);
    this.Timer.Tick += new EventHandler(Timer_Tick);
    this.Timer.Start();
}
 
private void Timer_Tick(object sender, EventArgs e)
{
    OutputTextBlock.Text = DateTime.Now.ToLongTimeString();
}

DispatcherTimer supports alto cancellation with the method Stop(). Please always remember that the computation is done in the UI Thread so if it is too long the better is downgrade to the Timer class and manage the marshaling by yourself.

#5 Using Reactive Extension

If you are still not satisfied by the tools I've listed to here, you will be for sure interested in the last example that is based on the use of the ReactiveExtension library. RX is a library developed by Microsoft Dev Labs with the scope of simplify asynchronous programming. The Reactive Extension library is created around a particular paradigm that is based on the ObserverPattern. Initially you can be doomed by the library because it introduces a completely new model of programming but when you start to understand the paradigm it becomes evident it is excellent. Here is an example that reply the scheduled timer of the recipe #4:

Observable.Interval(TimeSpan.FromSeconds(5))
    .ObserveOnDispatcher()
    .Subscribe(o => OutputTextBlock.Text = DateTime.Now.ToLongTimeString());
So, actually it is only a row of code and it does all the work: generate the interval, marshal to the UI and finally updates the TextBlock.

Thinking threaded

After this series of recipes about threading I would add some final words. When you start writing multi threaded code, no matter what is the tool you decide to use, it is important you switch your brain in multi thread mode. The tools you use may be perfect in specific cases as I've shown during the article, but the subtle problems that may born are completely up to you and to your experienced feeling.

About the Author

Andrea BoschinAndrea Boschin is 41 years old from Italy and currently lives and works in Treviso, a beautiful town near Venice. He started to work in the IT relatively late after doing some various jobs like graphic designer and school teacher. Finally he started to work into the web and learned by himself to program in VB and ASP and later in C# and ASP.NET. Since the start of his work, Andrea found he likes to learn new technologies and take them into the real world. This happened with ASP.NET, the source of his first two MVP awards, and recently with Silverlight, that he started to use from the v1.0 in some real projects.
 


Subscribe

Comments

  • -_-

    RE: Threading recipes: the many ways of spawning background works in Silverlight


    posted by ibebbs on May 18, 2011 12:32

    What about tasks from the Silverlight Async library? They're much more suitable for "spawning background works" than Rx.

  • -_-

    RE: Threading recipes: the many ways of spawning background works in Silverlight


    posted by Usama on May 19, 2011 08:05

    nice bro . is there any  improvement in Silverlight 5

Add Comment

Login to comment:
  *      *