Could someone please explain to me what is the difference in the behaviour of the two code samples below?
Which one is better/safer?
This is a snippet from a Repository implementation.
Task.Run
public Task<TDocument> FindByIdAsync(string id)
{
return Task.Run(() =>
{
var objectId = new ObjectId(id);
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
return _collection.Find(filter).SingleOrDefaultAsync();
});
}
Async Task
public async Task<TDocument> FindByIdAsync(string id)
{
var objectId = new ObjectId(id);
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
return await _collection.Find(filter).SingleOrDefaultAsync();
}
>Solution :
Task.Run, as said in the docs:
Queues the specified work to run on the
ThreadPooland returns a task orTask<TResult>handle for that work.
I.e. in this case it will force the execution of all the code on the thread pool. Usually Task.Run is used for CPU-bound workloads, for example if FindByIdAsync is executed within some single-threaded SynchronizationContext (for example in UI thread of a desktop app) and code before SingleOrDefaultAsync call is computational-heavy it can lead to UI freezes which is not desirable (also it will handle cases when SingleOrDefaultAsync is not "truly async").
On the other hand if you are calling this method in "vanilla" ASP.NET Core which does not have synchronization context there is not much point in using Task.Run in this case and the second approach should be preferable (it should be more performant, though not that much noticable in a lot of cases).
Also assuming that this is code used to query some database/storage and client highly likely is "truly async" (note that there are some nasty exceptions) then there is not much point in using first one over the second one.
See also:
- Asynchronous Programming in .NET – Introduction, Misconceptions, and Problems
- Asynchronous programming doc
- Task asynchronous programming model
- Recognize CPU-bound and I/O-bound work