Creating pre-completed Tasks

Creating pre-completed Tasks

  • Comments 22

 

We’ve been considering adding support for creating completed Tasks from an existing result.  Here’s a prototypical example of where this could be valuable.

 

void Task<float> ComputeAsync(...)
{
    if (!resultIsCached)
    {
        return Task<float>.Factory.StartNew(() => Compute());
    }
    else
    {
        // return a Task<float> with the cached result
    }
}


The method usually returns a Task<float> that represents some compute-intensive operation that will be done asynchronously.  However, the greater code has the ability to cache results from previous operations, so there’s a chance that the requested result is already available.  If that’s the case, we just want to return a completed Task with the cached result.  Note that this can be done in .NET 4 as follows:

 

TaskCompletionSource<float> tcs = new TaskCompletionSource<float>();
tcs.SetResult(cachedResult);
return tcs.Task;


But we could make this easier and slightly better-performing:

 

return Task.FromResult(cachedResult);


So your input would help.  If you’ve got a minute, feel free to answer the following questions and/or provide any other thoughts you have:

  1. Would the convenience make this feature worthwhile?
  2. If you have code that resembles this example, is performance a huge concern (to the point that shaving a few allocations and interlocked operations off of the creation of a pre-completed Task would help)?
  3. What about support for creating other pre-completed Tasks, e.g. creating a pre-canceled Task or one from an existing Exception?  Details regarding the scenario would help here.

Thanks!

 

Leave a Comment
  • Please add 7 and 4 and type the answer here:
  • Post
  • Shaving extra instructions off the parallel framework is nice, but the JIT is leaving millions of instructions unoptimized by not inlining operations on structs and being more aggressive on array access bounds checks!

  • Sebastian, structs are inlined starting from .NET 3.5 SP1.

    I've found recently that in 4.0 they are inlined even in generics!

  • I'm inclined to agree with Sebastian; saving execution time is always nice, but in this case, without having done any testing, the performance potentially gained by implementing a Task.FromResult() will probably be negligable compared to the cost of looking up the cached result (which might very well still be small compared to computing it anew).

    The brevity of expression having a single Task.FromResult() method call would be nice, but hardly something that makes or breaks code.

    By the way, maybe I'm missing something (I haven't done much dabbling with .NET 4 yet, to be honest), but wouldn't that be more like Task<float>.FromResult(cachedResult)?

  • I think it is a very good idea and something I missed in Lazy<T>.

    But essentially that will mean that you should allow any states to be created, since I *can* cache exception in certain cases. For example, if this is an exception from external source that makes it improbable that calculation will succeed if retried (such as 404).

  • @Michael: It would then probably become a generic method on the non-generic class. This way you can omit the generic type most of the time, because the compiler will infer it.

    For example: Tuple.Create(1, 2.5) is translated by the compiler to be Tuple.Create<int, double>(1, 2.5)

  • The idea is welcome, it enables to use Task<T> as a promise in a more convenient way.

    en.wikipedia.org/.../Promise_(programming)

  • I think it would be a nicer nomenclature, and the reduced instructions are always a win, but not necessarily a make it or break it feature.

  • If this can avoid the need to schedule the task into the ThreadPool and marshalling the result back into the Task<T> object there could be a serous gain. However the mechanism to recognised the computed result already exists would need to be slick enough for the gain to be seen.

    It is not immediately obvious whether this can be true often enough for the extra API to be worth it.

  • Customers can work around this by returning an Either<TResult, Task<TResult>> from the method.

  • Certainly a nice feature, but since new features start at -100 points [1] I don't see that it would do enough good to justify the costs :-)

    [1] blogs.msdn.com/.../57985.aspx

  • Thanks all for the feedback!  Seems the general opinion is "nice to have, but not critical".

  • I think this would be a nice feature - I've had a couple of times when I could have used this.  However, there is a fairly straightforward workaround (as you show), so it's not a critical feature.

    That being said, if you do implement this, it seems like it should be a method on TaskFactory, ie: "return TaskFactory.FromResult(cachedResult);"  This would correlate more closely with TaskFactory.FromAsync, and be a bit less confusing.

  • The feature should be there. The primary reason is discoverability. Too many people don't know or forget about TaskCompletionSource. The extra performance is a good reason too. After all, that's why you have the StartNew methods too, right?

    I do agree with Reed Copsey Jr tough that it should be on TaskFactory and TaskFactory<TResult>.

  • I've definitely used this pattern and have in fact created my own helper that does exactly what this new FromResult would do. It's not only useful in the case of cached values, but also when implementing an interface that expects you to return a task, but your implementation is "quick enough" to not actually benefit being started on a new thread.

  • I'd vote for this feature.

    I would also like to see a constant completed Task (non generic) object.  Kind of like string has a single Siring.Empty.

Page 1 of 2 (22 items) 12