Did I mention that I'm not the worlds leading expert on Windows Workflow Foundation? Well if I didn't please allow me to say it again.
Like you I'm just learning a lot of new stuff. To get my arms around WF I'm reading the book Windows Workflow Foundation Step By Step by Kenn Scribner. I've found the book to be pretty good and today I hit the chapter on the Replicator Activity. After working through a quick example with this activity I had a few questions about how exactly this thing works.
It has this intriguing property called ExecutionType which can be Sequential or Parallel. As I was pondering the possibilities (and pitfalls) of such a thing I decided it would be fun to create a sample application to illustrate exactly how this thing works. You can find the sample here.
After a few hours of great fun coding I learned a couple of interesting things...
When Parallel is not Concurrent.
When the ReplicatorActivity class is set to execute in Parallel, it doesn't really mean that the child activities contained with in will execute in parallel (as in concurrently on separate threads). Instead it means that it will call the initialize event once for each item in the list and then call the code activity once for each item in the list. I'm not sure what the advantage of this is really but perhaps someone can tell me why this is a good thing.
In my other reference book Essential Windows Workflow Foundation by Dharma Shukla and Bob Schmidt they point out that Workflow Instances are inherently single threaded because this makes the programming model much simpler. However, the runtime can manage many instances of workflows that are executing concurrently which gives you the concurrency you desire.
In fact if you run my sample application you will see the most amazing thing. You can start many, many instances of workflows that are all concurrently counting numbers. And what is more you can easily cancel any of them at any time. How cool is that!
Oh sure, you could write your own thread pool or deal with queuing up work items etc. But really to do this right it would take a ton of work but doing it with WF is pretty simple.
How to deadlock your UI thread
When building sample applications I always try to make them easy to see from the back of the room. One trick I have often used is to create a TraceListener that writes trace messages to a listbox. Of course these calls have to be marshaled to the correct thread by using Control.Invoke. Everything was working wonderfully until I added the Stop button to my UI which terminates a workflow. As soon as I did this the workflow thread would deadlock as it tried to output a trace to the listbox.
Fortunately I've just been through Juval Lowy's most excellent WCF Master Class where I learned about the .NET SynchronizationContext which Juval says is the best kept secret of .NET 2.0. This class allows me to Post a message to the UI thread instead of calling back into it (and causing a deadlock).
Is this the best way to solve the problem?
I'm not sure... it is *a* way to solve the problem but probably not the only way. It seems to me that a workflow updating a UI control is a very common scenario. Surely many people have run into this type of thing before. I'm curious how they solved the problem.
So Have Some Fun
If you have some moments to kill, check out my sample app. I think I'll make some more modifications tomorrow to illustrate saving and loading workflows as well.
I've been a bit afraid of WF for testability reasons (whether founded or unfounded). What's been your experience?
Testing is a bit less straightforward than your typical class but still very do-able. I've written a small example of testing with WF. I'll post it soon.
"And what is more you can easily cancel any of them at any time. How cool is that!"
Why would this be cool? To be able to cancel a workflow? Why would that be good? I think maybe I can imagine why...but?
Anytime you have a long running process in a UI you might want to have the option to cancel it. Cancelling a background operation is useful in many scenarios and workflow neatly encapsulates the proess into something that is logically described and managed for you. This is much nicer than managing your own background processes with a threadpool.