If you develop applications in Silverlight a lot, it becomes more and more annoying that Flex doesn’t support multi threading. This is particularly annoying when you are processing a long running task and need to display some progress. The problem is that you can’t do this task on a background thread, this means that the ui thread is busy processing your task and it can’t update the ui until your task is done, at which the progress of course is 100%. In this article I’m going to show how you can somewhat work around Flex’ and more precisely Flash’ lack of multithreading while still being able to let the ui be responsive and show progress.
The sample Flex application
The sample application in this blog post is a Flex application which turns an image into greyscale. A better option for this would be to use a custom filter built with PixelBender, but this example demonstrates the problem quite well and with PixelBender you lose the ability to report progress. Take a look at the screenshot below:
It has a nice photograph of a parrot, two buttons and a progressbar showing progress when the image is being converted to greyscale. Next up, the code behind:
At this point I encourage you to download the sample project here and run it. If you don’t have Flash Builder installed, I’ve exported a release build here which you can run by double clicking the .html file. First click on the “Grey it” button, you’ll see the ui freeze for a while, when the application has finished converting the image to greyscale, the ui unfreezes and the progressbar jumps to 100%. Next, click on the “Grey it with pauses” button. You’ll see the image being converted to greyscale gradually and you’ll see the progressbar showing progress, you’ll also notice that the “Grey it with pauses” takes more time to complete than the “Grey it” button. Let’s explain what happens:
- Take a look at line 67, this method get’s called when the “Grey it” button is clicked. It clones the original BitmapData (which gets saved on the creationComplete event), resets the progress method using the “setProgress()” method on line 62 and calls the “convertToGreyScale()” method which does all the work. You can see a problem indicated in the comment. The problem is that when supplying the “_parrot” Image component with the original colored image data, the user interface doesn’t update until the “convertToGreyScale()” method has completed. The only way to work around this is to build in an artificial delay using a Timer for example and experiment to find the correct delay in which the Image get’s updated. That’s why I previously told you to first click on the “Grey it” button and then on the “Grey it with pauses” button. The other way around you don’t see the image going from colored to greyscale.
- In the “convertToGreyScale()” method on line 23, the BitmapData data is processed and converted to grayscale using a standard algorithm, maybe it’s not the most optimized algorithm, but it suffices. Most people would use nested for loops, I’ve converted it to a single loop in order to explain the next part more easily. This method updates the progressbar once in a while, in total 16 times, this is determined on line 25. If I updated the progressbar every iteration, I would get a script timeout, even if this timeout was set to 60 seconds, which is the maximum. As you’ve seen, the user only sees the progressbar at 100% when this method is finished, because only then can Flash update the ui. In fact, all the calls to “setProgress()” could be removed from this function, because they don’t have any visual effect.
- Next take a look at the “convertToGreyScaleUsingPauses()” method on line 44. This method get’s called when the user clicks the “Grey it with pauses” button. The heart of this method is on line 48. The “UIUtilities.pausingFor()” method is called, in almost the same way as I configured the for loop in the “convertToGreyScale()” method. The loop body has become an anonymous function that must accept an int (the current iteration) and is called multiple times depending on the first two arguments. Next to the first anonymous function I supply another function which is responsible for setting the progress. The “pausingFor” function makes sure that pauses occur, in which the ui can update itself, hence the name “pausingFor”. Take a look at the code below:
This is the source code of the UIUtilities class. The only public static function is the “pausingFor” function. Take your time reading the ASDoc, it explains what this function does and what the different parameters are for. This method calls another private static function “executeLoop()” which has almost the same parameters as the “pausingFor” method, except for the “numberOfIterationsBeforePause” parameter. This parameter is the result of the “toExlusive” parameter divided by the “numberOfPauses” parameter of the “pausingFor” method.
Let’s take a look the “executeLoop()” method on line 34. I’ve written inline comments that explain most of what’s going on. The most important part of this function is on line 46. This is the “callLater()” method that any UIComponent has and this is one of the most overlooked methods in Flex. It accepts a function as the first parameter and an array with arguments to supply to that function as the second parameter. When you call this method, Flex schedules the supplied function to be called in the next frame. This way, the ui can update itself for the rest of the duration of the current frame. This means that there must be enough time left in the current frame and there mustn’t be any of your own code that needs to be executed after the call to “callLater()” , or else you still won’t see any ui updates. In the example above I supply an anonymous function that needs to be called in the next frame. In this anonymous function I do two things:
- Let the “loopBodyFunction” parameter execute.
- Afterwards call the “executeLoop()” function again. This way I’m sure that after the ui has had time to update itself, the “executeLoop()” function resumes with the correct iteration after being suspended by the “callLater()” method.
Another interesting part is on line 61. In the iteration just before the iteration in which the “callLater()” function is called, I give the user the chance to set any progress values that the ui needs for updating itself, by calling the “updateProgressFunction” parameter and supplying the number of iterations processed and the number of iterations that need to be processed. This way, I minimize the calls to the ui to when they matter the most: just before a call to “callLater()”.
With my “UIUtilities.pausingFor()” function, I’ve shown how you can let the ui still be responsive and let it update itself, in a way that’s quite similar to using a normal “for” loop in a background thread. My “UIUtilities” class can also be easily expanded with a “pausingForEach()” function, building on the “pausingFor” function. While the api of the “UIUtilities” class is quite simple, be aware that using this api has a performance cost, letting the task finish without giving the ui the time to update itself, is always faster, but not necessarily better for the user of the application. Also, this class is nowhere near a replacement for having the ability to do true multi threading, which would be better for the performance, certainly on multi core processors. But until we have that possibility in Flash, we have to work around the lack of multi threading using solutions similar to the solution provided above. While my post was titled with “Flex 4”, the solution provided above should also work on Flex 3. You can find the working solution with the source code, here.