Is using Task.Run a bad practice?

What you are doing is offloading a synchronous operation to another thread. If your thread is "special" then that's perfectly fine. One example of a "special" thread is a UI thread. In that case you may want to offload work off of it to keep the UI responsive (another example is some kind of listener).

In most cases however you're just moving work around from one thread to another. This doesn't add any value and does add unnecessary overhead.

So:

Is the pattern I've depicted above actually a bad idea?

Yes, it is. It's a bad idea to offload synchronous work to the ThreadPool and pretend as if it's asynchronous.

Am I losing some of the functionality/ intended purpose of async calls doing it this way?

There's actually nothing asynchronous about this operation to begin with. If your executing this on a remote machine and you can benefit from doing it asynchronously the operation itself needs to be truly asynchronous, meaning:

var myObject = await unitOfWork.MyObjects.SingleAsync(o => o.Name == objectName);

What you're currently doing is called "async over sync" and you probably shouldn't do it. More in Should I expose asynchronous wrappers for synchronous methods?


Recently I've read several SO posts that suggest using Task.Run() is a bad idea, and should only be used under certain circumstances, but those circumstances did not seem very clear.

The absolutely bare bones rules of thumb I tell people who are new to asynchrony is:

First, understand the purpose. Asynchrony is for mitigating the important inefficiencies of high-latency operations.

Is the thing you're doing low-latency? Then don't make it asynchronous in any way. Just do the work. It's fast. Using a tool to mitigate latency on low-latency tasks is just making your program unnecessarily complex.

Is the thing you're doing high-latency because it is waiting on a disk to spin or a packet to show up? Make this asynchronous but do not put it on another thread. You don't hire a worker to sit by your mailbox waiting for letters to arrive; the postal system is already running asynchronously to you. You don't need to hire people to make it more asynchronous. Read "There Is No Thread" if that's not clear.

Is the high-latency work waiting on a CPU to do some enormous computation? Like a computation that is going to take well over 10 ms? Then offload that task onto a thread so that the thread can be scheduled to an idle CPU.


public Task<Guid> GetMyObjectIdAsync(string objectName)

When I see this, I expect there to be some advantage in using this method rather than just wrapping it in Task.Run() myself.

In particular, I'd expect it to release the thread when it hits some I/O or otherwise has the opportunity to do so.

Now consider if I have the code:

_resource = GetResourceForID(GetMyObjectIdAsync(SomeLongRunningWayToGetName()));

If I have a reason to need to have this done in a task, and I'm in the sort of situation where Task.Run() does actually make sense (I have a reason to offload it onto another thread) the best way to do this would be to wrap the whole thing:

Task task = Task.Run(() => _resource = GetResourceForID(GetMyObjectIdAsync(SomeLongRunningWayToGetName())));

Here Task.Run() might be a bad idea for me as the caller, or it might be good because I really am gaining from what it gives me.

However, if I see your signature I'm going to think that the best way to do this with your code would be to turn it into code that uses that method.

Task task = SomeLongRunningWayToGetName()
  .ContinueWith(t => GetMyObjectIdAsync(t.Result))
  .ContinueWith(t => _resource = GetResourceForIDAsync(t.Result));

(Or similar using async and await).

At best this has less good chunking of the Task.Run(). At worse I'm awaiting this just to gain from the better asynchronicity that it doesn't offer in a context that could make use of it if it was really there. (E.g I might have used this in an MVC action that I'd made asynchronous because I thought the extra overhead would be repaid in better thread-pool use).

So while Task.Run() is sometimes useful, in this case it's always bad. If you can't offer me greater asynchronicity than I can bring to the use of the class myself, don't lead me to believe you do.

Only offer a public XXXAsync() method if it really does call into asynchronous I/O.

If you really need to stub out an asynchronous method to e.g. match a signature of a shared base or interface, then it would be better as:

public Task<Guid> GetMyObjectIdAsync(string objectName)
{
  return Task.FromResult(GetMyObjectId(objectName);
}

This is bad too (the caller would still have been better off just calling GetMyObjectId() directly), but at least if code awaits it then while it operates on the same thread there's no overhead of using yet another thread to do the work, so if it's mixed in with other awaits the negative impact is reduced. It's therefore useful if you really need to return a Task but can't add anything useful in how you call it.

But if you don't really need to offer it, just don't.

(A private method calling Run() because you every call site benefits from it is different, and there you're just adding convenience rather than calling Run() in several places, but that should be well-documented as such).