问题
C# provides two ways of creating asynchronous methods:
Method 1:
static Task<string> MyAsyncTPL() {
Task<string> result = PerformWork();
return result.ContinueWith(t => MyContinuation());
}
Method 2:
static async Task<string> MyAsync() {
string result = await PerformWork();
return MyContinuation();
}
Both the above methods are async and achieves the same thing. So, when should I choose one method over the other? Are there any guidelines or advantages of using one over the other?
回答1:
I recommend you use await rather than ContinueWith. While - at a high level - they are very similar, they also have different default behavior.
When you use ContinueWith, you are choosing a lower-level abstraction. In particular, here are some "danger points", and this is why I don't recommend using ContinueWith unless the method is really simple (or your name is Stephen Toub):
- Exceptions raised from
async Taskmethods are placed on the returned task; exceptions raised from non-asyncmethods are propagated directly. awaitwill by default will resume theasyncmethod in the same "context". This "context" isSynchronizationContext.Currentunless it isnull, in which case it isTaskScheduler.Current. This means that if you callMyAsyncon a UI thread (or within an ASP.NET request context), thenMyContinuationwill also execute on the UI thread (or in that same ASP.NET request context). I explain this more on my blog.- You should always specify a scheduler for
ContinueWith; otherwise, it will pick upTaskScheduler.Current, which can cause surprising behavior. I describe this problem in detail on my blog. That post is aboutStartNew; butContinueWithhas the same "non-default default scheduler" problem described in that post. awaituses appropriate behavior and optimization flags that are not set by default inContinueWith. For example, it usesDenyChildAttach(to ensure asynchronous tasks are not mistakenly used as parallel tasks) andExecuteSynchronously(an optimization).
In short, the only reason to use ContinueWith for asynchronous tasks is to save an extremely small amount of time and memory (by avoiding the async state machine overhead), and in exchange your code is less readable and maintainable.
With an extremely simple example, you might get away with it; but as Jon Skeet pointed out, as soon as you have loops the ContinueWith code simply explodes in complexity.
回答2:
await is basically a shorthand for the continuation, by default using the same synchronization context for the continuation.
For very simple examples like yours, there's not much benefit in using await - although the wrapping and unwrapping of exceptions makes for a more consistent approach.
When you've got more complicated code, however, async makes a huge difference. Imagine you wanted:
static async Task<List<string>> MyAsync() {
List<string> results = new List<string>();
// One at a time, but each asynchronously...
for (int i = 0; i < 10; i++) {
// Or use LINQ, with rather a lot of care :)
results.Add(await SomeMethodReturningString(i));
}
return results;
}
... that gets much hairier with manual continuations.
Additionally, async/await can work with types other than Task/Task<T> so long as they implement the appropriate pattern.
It's worth reading up more about what it's doing behind the scenes. You might want to start with MSDN.
来源:https://stackoverflow.com/questions/22997523/difference-between-task-and-async