问题
I've been reading Eric Lippert's blog posts on Asynchrony in C# 5 (part 4 being particular relevant) and have watched Anders PDC10 talk on the subject and I'm unclear on how continuations from asynchronous methods are resumed in a single threaded context.
Both sources discuss using asynchronous methods in a single threaded UI loop to improve responsiveness and in Anders' example he mentions that when an asynchronous task completes it's continuation is scheduled by the addition of a message to the message pump.
Does the asynchronous method really know that it needs to perform what seems like a context specific action, or was this a simplification?
More generally, how can resumption from asynchronous methods handled in a single threaded context? Is there a requirement for scheduling within a single thread?
回答1:
The task continuation knows where the continuation needs to be scheduled - e.g. "any thread pool thread" or "the UI thread".
This behaviour is determined by the "awaiter", however - it's not actually part of what the C# compiler is responsible for. The compiler just calls BeginAwait
and passes in the continuation; the awaiter returns a Boolean value indicating whether the task has already completed synchronously, or whether the caller should return and let the continuation occur asynchronously.
So for the moment, that decision is made in the awaiter returned by TaskEx
- but I wouldn't be surprised to see it all get bundled into Task
eventually. That can flow things like the synchronization context, which knows how further actions should be handled.
I'm not quite sure what sort of genuinely single-threaded context you're considering... or are you thinking of a situation where the bulk of the work needs to happen in a single thread, but other threads can be involved for the asynchronous bit (e.g. when an HTTP packet is received, processed on an IO completion port thread, and the response handled back on the UI thread)?
回答2:
Jon's answer is of course great; I thought I'd just add one more thing.
Consider a WinForms application with a single button on a form that runs code when you click the button.
What happens when you are not clicking the button? Nothing. The process exists, the code is running, but it doesn't seem to be doing anything. In fact what it is doing is processing messages on the UI thread and determining that none of them are interesting, but it doesn't look like it is doing anything interesting.
When you click the button, suddenly one of those messages is interesting, and the message pump knows that when it sees that click event, it should run some code. So it does.
The asynchrony-on-a-single-thread scenario is just the same. The continuation -- the "what to do after the task is finished" code is effectively an "event handler" of the "task is finished" event. When the task finishes, it "pushes the button" and enqueues a message on the message queue of the UI thread. It doesn't matter whether it does so from the UI thread or from an I/O completion thread or whatever. When the UI thread gets around to processing that message, it invokes the continuation. Just the same as when the UI thread gets around to processing a button click, it invokes the click handler.
来源:https://stackoverflow.com/questions/5377532/how-is-resumption-from-await-implemented