Reputation: 16002
Given the following snippet:
public Task StartReading()
{
var activityCheck = Task.Factory.StartNew(async () => await this.CheckActivityTimeout(), this._token.Token).Unwrap();
var reading = Task.Factory.StartNew(async () => await this.ReadAsync(), this._token.Token).Unwrap();
// for reference, this code produces the same result:
// var activityCheck = this.CheckActivityTimeout();
// var reading = this.ReadAsync();
return Task.WhenAny(reading, activityCheck);
}
When an exception is thrown in CheckActivityTimeout
, I am catching it as follows.
var read = StartReading()
var tasks = new Task[] { read, taskx, tasky, taskz };
int completed = Task.WaitAny(tasks);
var r = tasks[completed];
r
does not have it's exception set. Instead, if I look at the debugger, I find that the task r
has the exception stored within a Result
property. How do I get to this actual result?
r
has type Id = 17, Status = RanToCompletion, Method = "{null}", Result = "System.Threading.Tasks.UnwrapPromise``1[System.Threading.Tasks.TaskExtensions+VoidResult]"
You can see that the actual exception is inside the result of the inner task. How do I propogate it upwards?
r.Exception == null
r.Result
is inaccessible.
update
var r = Task.WhenAny(tasks).Result; // produces exactly the same wrapped result!
In the debugger it looks like this:
Upvotes: 4
Views: 475
Reputation: 16002
I have found that another solution to the problem is just to cast the result to Task<Task>
Casting the result of Task.WhenAny
will also obtain the inner task.
var result = Task.WhenAny(reading, activityCheck).Result;
var inner = ((Task<Task>)result).Result;
inner.Exception...
Upvotes: -1
Reputation: 456417
Your problem is because of how Task.WhenAny
works. Task.WhenAny
returns a task whose result is the task that completed. This is why read.Result
is a task, which in turn contains the exception.
It's not really clear what the desired semantics are, but if you want StartReading
to surface the result of the first completed task, you can use a "double await", like this:
public async Task StartReadingAsync()
{
var activityCheck = this.CheckActivityTimeout();
var reading = this.ReadAsync();
await await Task.WhenAny(reading, activityCheck);
}
On a side note, don't use StartNew
. If you have CPU-bound (or other blocking code) that you need to move off the UI thread, then use Task.Run
; otherwise, just call the methods directly.
Upvotes: 6