As I mentioned before, writing high-performance, multithread, extensible, expansible and scalable software is actually very challenging. Hardware improvements can be worthless unless you know how to take advantage of them.
Now, talking about multithreading, WF has a composite activity that can execute child activities in parallel (at least, in theory). The idea is that branches execute in a “parallel fashion”:
The first thing to note is that the order in which child activities execute is non-deterministic. That is, it’s not guaranteed that all the activities in the first branch get executed before the second branch gets executed. It’s not guaranteed that the WF runtime will execute the first activity of each branch, then the second and so on. The only guarantee is that a “branch switch” won’t happen until a single activity is finished (e.g.: if you have a code activity, there won’t be a “branch switch” until the thread finishes processing all the code in the activity).
“So far so good”.
Moving on, whenever we talk about “parallel execution”, we always think of execution in a multithreaded sense. This isn’t truly the case. The WF runtime uses only a single thread from the threadpool to run all the branches. This means that the parallel execution paths do not execute concurrently, and what we have here is actually cooperative multithreading. The question is: why use only one thread? I mean, why can’t we specify that one branch should run in one thread and all the others in another? Imagine that we have an activity in one of the branches that will actually take a long time, for instance, a web request?
Looking to the basics of WF, doing what I’m suggesting will actually “break” the idea of an Activity as the basic unit of work. What I’m suggesting will allow a “branch switch” in the middle of activities execution. A code activity might be interrupted due to a context switch.
I think, though, that this would be a nice feature.
Nevertheless, you can “do it yourself”: asynchronous programming model, threadpool...