The Progress sample in the CTP suggested ways to do it. Here, we're using TaskEx. Run to do the work on the background thread. Whenever it wants to update the UI, it does that through the progress object. The "while p. NextProgress" ends up running on the UI thread. The trouble is that an exception-safe scope would ultimately need to do some kind of "await" to switch back to the context in its finally block. It's not just a language limitation at play here: there just isn't a good CLR way to achieve that.
I should note that "await XxxAsync " currently does exactly that: it saves the sync context, then runs the task XxxAsync, then returns back to the async context. We wanted to avoid having XxxAsync also do the same work, since that felt like too much overhead. Run automatically handle exception safety for you". Would you mind elaborating please, Cory?
Okay, that makes sense. Still, I hope you guys find a better way to do it. Third, DoEvents poses the very real danger of unexpected reentrancy. The non-concurrent nature of the DoEvents technique is attractive, but clearly not quite the right solution for a complex program. A better idea is to break down the items on the checklist into a series of short tasks, each of which can be completed rapidly enough that the application can appear to be responsive to events. That idea is nothing new; dividing a complex problem into small parts is why we have subroutines in the first place.
The interesting twist is that instead of rigidly running down a checklist to determine what has already been done and what needs to be done next, and only returning control to the caller when everything is completed, each new task is given the list of work that must come after it. When a task has finished, it can look at the continuation and finish it off right there. Or it might schedule the continuation to run later. If the continuation requires information computed by the previous task, the previous task can pass that information along as an argument to the call that invokes the continuation.
With this approach, the total body of work is essentially broken up into little pieces that can each be executed rapidly. The system appears responsive because pending events can be detected and handled between the executions of any two of the small pieces of work.
New long-running tasks are not dealt with immediately, but they are queued up for eventual processing. The essential difficulty is determining how to tell each small unit of work what its continuation is; that is, what work needs to come next. Now ServeBreakfast returns immediately after PrepareAsync returns; whatever code called ServeBreakfast is then free to service other events that occur.
Moreover, PrepareAsync also ensures that the callback method will be invoked with the prepared meal as its argument at some time after the meal preparation task is completed. Note that none of this necessarily involves a second thread.
Perhaps PrepareAsync causes the meal preparation work to be done on a separate thread, or perhaps it causes a series of short tasks associated with meal preparation to be queued up on the UI thread to be executed later.
We could solve this larger problem if we had an asynchronous version of each of these methods. What would the resulting program look like? Remember, each method must be given a callback that tells it what to do when the unit of work is completed:.
You end up essentially turning your program inside-out; the code now emphasizes how all the callbacks are wired together, and not what the logical workflow of the program should be. Callback-based asynchrony does keep the UI thread responsive and minimize time wasted by synchronously waiting for long-running work to complete. But the cure seems worse than the disease. The price you pay for responsiveness and performance is that you have to write code that emphasizes how the mechanisms of the asynchrony work while obscuring the meaning and purpose of the code.
The upcoming versions of C and Visual Basic instead allow you to write code that emphasizes its meaning and purpose, while giving enough hints to the compilers to build the necessary mechanisms for you behind the scenes. The solution has two parts: one in the type system , and one in the language. Precisely how the result of type T is going to be produced in the future is an implementation detail of a particular task; the work might be farmed out to another machine entirely, to another process on this machine, to another thread, or perhaps the work is simply to read a previously cached result that can be accessed cheaply from the current thread.
The language half of the solution is the new await keyword. Sign up the remainder of the current method as the callback associated with the continuation of that task. I then found this blurb here :. The reason we got rid of it was because it was so dangerous. The alternative is to bundle up your code inside TaskEx. My question is simply: Why was it dangerous?
What specific dangers would using it lead to? Note that I did read the rest of that post, so I do understand there are technical limitations here. My question is still, if I'm aware of this, why is it dangerous? I am considering reimplementing helper methods to give me the specified functionality, but if there is something fundamentally broken, other than that someone decided it was dangerous, I would not do it. Stephen Toub has some more information on the reasoning in this thread.
0コメント