Also... I often use this method call in a loop, from multiple places. If these methods are now marked async, i'm afraid I might be reading data off a stream when I shouldn't be, which will cause all sorts of nightmares. Is it a problem or am I thinking too far ahead?
If you always use await
whenver you call *Async
methods, then your async
methods will act just like synchronous methods (except they won't block). So using await
in a loop will work just like you expect.
async
does indeed "grow" through the code base. I usually think of this as similar to the old story about "turtles all the way down"; others have called it a "zombie virus".
I describe the deadlock situation in detail on my blog. As I state there, the best option is to allow async
to grow.
If you must create a synchronous wrapper for an asynchronous method, see Stephen Toub's advice. You can use Task.Result
, but you need to do two things:
- Use
ConfigureAwait(false)
everywhere. This will sidestep the deadlock situation.
- Be aware that
Result
has different error handling semantics.
For your particular example, something like this should suffice:
private async Task<string> ReadLineAsync()
{
... // *Every* await in this method and every method it calls
// must make use of ConfigureAwait(false).
}
public string ReadLine()
{
try
{
return ReadLineAsync().Result;
}
catch (AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
throw;
}
}
Please carefully consider the complexity before choosing a mixed synchronous/asynchronous code base. It's not as easy as it first appears.
P.S. Generally speaking, TCP/IP code should all be asynchronous anyway. It's usually a good idea to have a continuous asynchronous read going on a socket.