Reputation: 2180
I have recently discovered by accident that I don't need to await a Task to get the exceptions that Task throws for program control logic.
Pre discovery of this feature, my code would look like this:
using (TcpClient client = new TcpClient())
{
try
{
var ca = client.ConnectAsync("192.168.1.88", 9999);
await await Task.WhenAny(ca, Task.Delay(5000));
if (ca.IsCompleted)
Console.WriteLine("Connected");
else
Console.WriteLine("Connection failed due to timeout");
}
catch
{
Console.WriteLine("Connection failed.");
}
}
Post discovery my code looks like this, (which personally I find more readable)
using (TcpClient client = new TcpClient())
{
var ca = client.ConnectAsync("192.168.1.88", 9999);
await Task.WhenAny(ca, Task.Delay(5000));
if (ca.IsFaulted || !ca.IsCompleted)
Console.WriteLine("Connection failed due to timeout or exception");
else
Console.WriteLine("Connected");
}
Is there anything technically wrong with intentionally leaving the unobserved exceptions to be ignored by the TaskScheduler
? E.g., will they back up and cause other problems in the async/await framework?
The behavior is further described in this question.
Upvotes: 1
Views: 846
Reputation: 456977
E.g., will they back up and cause other problems in the async/await framework?
No. Discarding a task will not cause any "back up" issues like that. In fact, most usages of Task.WhenAny
will result in a task being discarded.
As Marc pointed out in the comments, this approach does have some additional overhead: the task needs to be finalized and the exception is re-raised on a global event. So it is inefficient.
Is there anything technically wrong with intentionally leaving the unobserved exceptions to be ignored by the TaskScheduler?
As GSerg pointed out in the comments, there is a race condition in the code. Since the code doesn't actually use the "which task completed" result of WhenAny
, it may time out and then the connection completes, and the code would take the Connected
path. So I'd say this race condition is benign. But if you use this pattern elsewhere, think hard about the race conditions in that code, and whether they're acceptable.
There are some other inefficiencies in the code as well (in addition to the finalizer / unhandled exception event handler mentioned by Marc):
If this is a desktop/console app, then you may be able to ignore these kinds of inefficiencies. If this is a server app, you'd probably want to tighten it all up.
TL;DR: The code would work, but has some inefficiencies and a race condition (benign in this case). So this pattern would only be appropriate in some application situations. That's why you don't see this kind of pattern a lot.
Upvotes: 2