In general, it's often best to try to avoid spinning up new tasks if you are already working with task-based methods. Chaining tasks instead of blocking explicitly will reduce the overhead of the system, as it won't keep a ThreadPool thread tied up waiting.
That being said, it's often simpler to just block as you're doing.
Note that C# 5 makes this far simpler, providing an API that gives you the best of both:
public async Task<string> GetSomeData(CancellationToken token)
{
token.ThrowIfCancellationRequested();
var initialData = await SomeOtherMethodWhichReturnsTask(token);
string result = await initialData.MethodWhichAlsoReturnsTask(token);
return result;
};
Edit after update:
Given the new code, there isn't an easy way to chain this directly with ContinueWith
. There are a couple of options. You can use Unwrap to convert the Task<Task<string>>
you'd create, ie:
public Task<string> GetSomeData(CancellationToken token)
{
Task<Task<string>> task = GetSomeInteger(token)
.ContinueWith(t =>
{
return GetSomeString(t.Result, token);
}, token);
return task.Unwrap();
}
Alternatively, you can handle the unwrapping yourself elegantly with TaskCompletionSource<T>
:
public Task<string> GetSomeData(CancellationToken token)
{
var tcs = new TaskCompletionSource<string>();
Task<int> task1 = GetSomeInteger(token);
Task<Task<string>> task2 = task1.ContinueWith(t => GetSomeString(t.Result, token));
task2.ContinueWith(t => tcs.SetResult(t.Result.Result));
return tcs.Task;
}
This allows the entire process to work without creating a new Task (which ties up a threadpool thread), and without ever blocking.
Note that you would probably want to add continuations on cancellation, and use tcs.SetCancelled when a cancellation was requested, as well.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…