I'm sorry if this is a silly question (or a duplicate).
I have a function A:
public async Task<int> A(/* some parameters */)
{
var result = await SomeOtherFuncAsync(/* some other parameters */);
return (result);
}
the I have another function B, calling A but not using the return value:
public Task B(/* some parameters */)
{
var taskA = A(/* parameters */); // #1
return (taskA);
}
Note that B is not declared async and is not awaiting the call to A. The call to A is not a fire-and-forget call - B is called by C like so:
public async Task C()
{
await B(/* parameters */);
}
Notice that at #1, there's no await. I have a coworker that claims that this makes the call to A synchronous and he keeps coming up with Console.WriteLine logs that seemingly prove his point.
I tried pointing out that just because we don't wait for the results inside B, the task chain is awaited and the nature of the code inside A doesn't change just because we don't await it. Since the return value from A is not needed, there's no need to await the task at the call site, as long as someone up the chain awaits it (which happens in C).
My coworker is very insistent and I began to doubt myself. Is my understanding wrong?
You are right. Creating a task does only that and it does not care when and who will await its result. Try putting await Task.Delay(veryBigNumber); in SomeOtherFuncAsync and the console output should be what you would expect.
This is called eliding and I suggest you read this blogpost, where you can see why you should or should not do such thing.
Also some minimal (little convoluted) example copying your code proving you right:
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine($"Start of main {Thread.CurrentThread.ManagedThreadId}");
var task = First();
Console.WriteLine($"Middle of main {Thread.CurrentThread.ManagedThreadId}");
await task;
Console.WriteLine($"End of main {Thread.CurrentThread.ManagedThreadId}");
}
static Task First()
{
return SecondAsync();
}
static async Task SecondAsync()
{
await ThirdAsync();
}
static async Task ThirdAsync()
{
Console.WriteLine($"Start of third {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(1000);
Console.WriteLine($"End of third {Thread.CurrentThread.ManagedThreadId}");
}
}
This writes Middle of main before End of third, proving that it is in fact asynchronous. Furhtermore you can (most likely) see that the ends of functions run on different thread than the rest of the program. Both beginnings and the middle of main will always run on the same thread because those are in fact synchrnous (main starts, calls the function chain, third returns (it may return at the line with the await keyword) and then main continues as if there was no asynchronous function ever involved. The endings after the await keywords in both functions may run on any thread in the ThreadPool (or in synchronization context you are using).
Now it is interesting to note, that if Task.Delay in Third did not take very long and actually finished synchronously, all of this would run on a single thread. What's more, even though it would run asynchronously, it might all run on a single thread. There is no rule stating that an async function will use more than one thread, it may very well just do some other work while waiting for some I/O task to finish.