简体   繁体   English

使用 Task.Run 是不好的做法吗?

[英]Is using Task.Run a bad practice?

Generally, when implementing an asynchronous method on a class, I write something like this:通常,在类上实现异步方法时,我会这样写:

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

private Guid GetMyObjectId(string objectName)
{
    using (var unitOfWork = _myUnitOfWorkFactory.CreateUnitOfWork())
    {
        var myObject = unitOfWork.MyObjects.Single(o => o.Name == objectName);
        return myObject.Id;
    }
}

This sort of pattern allows me to use the same logic synchronously and asynchronously, depending on the situation (most of my work is in an old code base, not a lot supports async calls yet), as I could expose the synchronous method publicly and get maximum compatibility if I need to.这种模式允许我根据情况同步和异步使用相同的逻辑(我的大部分工作都在旧代码库中,还没有很多支持异步调用),因为我可以公开公开同步方法并获得如果我需要,最大的兼容性。

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.最近我读了几篇 SO 帖子,建议使用Task.Run()是一个坏主意,应该只在某些情况下使用,但这些情况似乎不太清楚。

Is the pattern I've depicted above actually a bad idea?我上面描述的模式实际上是个坏主意吗? Am I losing some of the functionality/ intended purpose of async calls doing it this way?我这样做是否会失去异步调用的某些功能/预期目的? Or is this a legit implementation?或者这是一个合法的实现?

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. “特殊”线程的一个示例是 UI 线程。 In that case you may want to offload work off of it to keep the UI responsive (another example is some kind of listener).在这种情况下,您可能希望卸载它的工作以保持 UI 响应(另一个示例是某种侦听器)。

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.将同步工作卸载到ThreadPool并假装它是异步的,这是一个坏主意。

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.最近我读了几篇 SO 帖子,建议使用 Task.Run() 是一个坏主意,应该只在某些情况下使用,但这些情况似乎不太清楚。

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?高延迟工作是否在等待 CPU 进行大量计算? Like a computation that is going to take well over 10 ms?就像一个需要超过 10 毫秒的计算? Then offload that task onto a thread so that the thread can be scheduled to an idle CPU.然后将该任务卸载到一个线程上,以便可以将该线程调度到空闲 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.当我看到这个时,我希望使用这种方法有一些优势,而不是自己将它包装在Task.Run()

In particular, I'd expect it to release the thread when it hits some I/O or otherwise has the opportunity to do so.特别是,我希望它在遇到某些 I/O 或有机会这样做时释放线程。

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.Run()确实有意义的情况(我有理由将其卸载到另一个线程),那么最好的方法是这样做是为了包装整个事情:

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.在这里Task.Run()对我作为调用者来说可能是一个坏主意,或者它可能是好的,因为我真的从它给我的东西中Task.Run()

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 ). (或类似使用asyncawait )。

At best this has less good chunking of the Task.Run() .充其量这对Task.Run()分块不太好。 At worse I'm await ing 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.更糟糕的是,我正在await它只是为了从更好的异步性中获益,如果它真的存在,它在可以利用它的上下文中没有提供。 (Eg 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). (例如,我可能在 MVC 操作中使用了它,因为我认为额外的开销会在更好的线程池使用中得到补偿)。

So while Task.Run() is sometimes useful, in this case it's always bad.因此,虽然Task.Run()有时很有用,但在这种情况下它总是很糟糕。 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.仅当它确实调用异步 I/O 时才提供公共XXXAsync()方法。

If you really need to stub out an asynchronous method to eg 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 await s 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 await s the negative impact is reduced.这也很糟糕(调用者还是直接调用GetMyObjectId()更好),但至少如果代码await ,那么当它在同一个线程上运行时,没有使用另一个线程来完成工作的开销,所以如果它与其他await混合在一起,负面影响就会减少。 It's therefore useful if you really need to return a Task but can't add anything useful in how you call it.因此,如果您确实需要返回一个Task但不能在您调用它的方式中添加任何有用的东西,它会很有用。

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). (调用Run()私有方法,因为您每个调用站点从中受益的方法都不同,并且您只是增加了便利性,而不是在多个地方调用Run() ,但这应该有详细记录)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM