A surprise TaskCancelledException
All of a sudden, my code started getting a lot of TaskCancelledException. It took me a while to figure out what was going on. We can imagine that the code looked like this:
var unwrap = Task.Factory.StartNew(() => { if (DateTime.Now.Month % 2 != 0) return null; return Task.Factory.StartNew(() => Console.WriteLine("Test")); }).Unwrap(); unwrap.Wait();
The key here is that when Unwrap is getting a null task, it will throw a TaskCancelledException, which was utterly confusing to me. It make sense, because if the task is null there isn’t anything that the Unwrap method can do about it. Although I do wish it would throw something like ArgumentNullException with a better error message.
The correct way to write this code is to have:
var unwrap = Task.Factory.StartNew(() => { if (DateTime.Now.Month % 2 != 0) { var taskCompletionSource = new TaskCompletionSource<object>(); taskCompletionSource.SetResult(null); return taskCompletionSource.Task; } return Task.Factory.StartNew(() => Console.WriteLine("Test")); }).Unwrap(); unwrap.Wait();
Although I do wish that there was an easier way to create a completed task.
Comments
The Async CTP has a static TaskEx.FromResult method that does exactly what you do:
public static Task<TResult> FromResult<TResult>(TResult result) { TaskCompletionSource<TResult> source = new TaskCompletionSource<TResult>(result); source.TrySetResult(result); return source.Task; }
Expect this TaskEx class to be merged with the Task class for .Net 5.
3 lines of code that can be extracted into some static property hardly counts as a difficult way to create a completed task ;)
Oren, have you gotten in Rx yet (as in fully grokked it)? You seem to use tasks a lot. I myself use Rx for most of my asynchronous needs. It is usually a better concept. And regarding this case, it has Observable.Empty or Observable.Return(null) helpers which could your case, had you been using Rx.
Omer, I would love to see some real examples of TPL vs. RX code, because I haven't really been able to figure out that. And I think that with C# 5.0, TPL is a better alternative
This is from SO: http://stackoverflow.com/questions/2542764/tpl-v-s-reactive-framework In general, tasks abstract single (future) values/computations and observables abstract streams of (future) values/computations. You could treat the single value case as a special case of a single value stream, and only use Rx. However if your use case primarily deals with single values, TPL might be a better feat. Also, Rx allows for much better composition (using LINQ).
From my experience, Rx works best when replacing events.
For me - Rx was a mind enhancing experience, which changed the way I perceive many problems. I advise you to take a leap of faith here, and invest some time in this ingenious framework.
Omer, Most of what we are doing are actually do computation of a single value. For example, take a look at the type of things we do here:
public Task<BatchResult[]> BatchAsync(ICommandData[] commandDatas) { var metadata = new RavenJObject(); AddTransactionInformation(metadata); var req = jsonRequestFactory.CreateHttpJsonRequest(this, url + "/bulk_docs", "POST", metadata, credentials, convention); var jArray = new RavenJArray(commandDatas.Select(x => x.ToJson())); var data = jArray.ToString(Formatting.None);
}
can you should me how this would be written, Rx style?
I'll give it a shot later. I guess the basic idea would be to treat it as a stream of command batches, and project it (select) into a stream of responses. Along the way we could push the handling into a dedicated thread, or use the thread/task pool. we might also easily "batch our batches" based on count or time if that could increase our performance. we might also throttle against overloading / DoS attacks for example.
I cross-posted your example in the Rx forums, for help getting an expert opinion on this (http://social.msdn.microsoft.com/Forums/en/rx/thread/3cd72a7e-fba8-4c81-9113-36ef78e2ac54).
Omer, None of those things are actually needed or required.
BatchAsync is being called from SaveChangesAsync, and there is usually only ever to be one of them.
Comment preview