gpt4 book ai didi

c# - Task.Run 或 TaskFactory.StartNew 是否始终不适合在异步方法中使用?

转载 作者:行者123 更新时间:2023-12-02 06:21:25 26 4
gpt4 key购买 nike

我听说线程的责任应该由应用程序承担,我不应该在异步方法中使用 Task.Run 或 TaskFactory.StartNew 。

但是,如果我有一个库,其中的方法执行相当繁重的计算,那么要释放例如接受 asp .net core http 请求的线程,我不能使该方法异步并使其长时间运行吗任务?或者这应该是一个同步方法,并且 asp .net core 应用程序应该负责启动任务?

最佳答案

首先我们想一下为什么需要异步?

Asynchrony is needed either for scalability or offloading.

可扩展性的情况下,公开该调用的异步版本不会执行任何操作。因为您通常仍然消耗与同步调用它相同数量的资源,甚至更多。但是,可扩展性是通过减少您使用的资源量来实现的。并且您不会通过使用 Task.Run() 来减少资源。

卸载的情况下,您可以公开同步方法的异步包装器。因为它对于响应能力非常有用,因为它允许您将长时间运行的操作卸载到不同的线程。这样,您就可以从方法的异步包装器中获得一些好处。

结果:

使用简单的异步外观包装同步方法不会产生任何可扩展性优势,但会产生卸载优势。但在这种情况下,通过仅公开同步方法,您可以获得一些不错的好处。例如:

  • 图书馆的表面积减少。
  • 您的用户会知道使用公开的异步 API 是否确实具有可扩展性优势
  • 如果同步方法及其周围的异步包装器都公开,开发人员就会面临这样的想法:出于可扩展性(?)原因,他们应该调用异步版本 ,但实际上,在没有可扩展性优势的情况下,支付额外的卸载开销实际上会损害其吞吐量。

来源是Should I expose asynchronous wrappers for synchronous methods?作者:斯蒂芬·托布。我强烈建议您阅读它。

更新:

评论中的问题:

那篇文章通过一个例子很好地解释了可扩展性。让我们考虑一下Thread.Sleep。有两种可能的方法来实现该调用的异步版本:

public Task SleepAsync(int millisecondsTimeout)
{
return Task.Run(() => Sleep(millisecondsTimeout));
}

另一个新的实现:

public Task SleepAsync(int millisecondsTimeout)
{
TaskCompletionSource<bool> tcs = null;
var t = new Timer(delegate { tcs.TrySetResult(true); }, null, –1, -1);
tcs = new TaskCompletionSource<bool>(t);
t.Change(millisecondsTimeout, -1);
return tcs.Task;
}

这两种实现都提供相同的基本行为,都是在超时到期后完成返回的任务。然而,从可扩展性的角度来看,后者的可扩展性要好得多。前一种实现在等待时间内消耗线程池中的一个线程,而后者仅依靠高效的计时器在持续时间到期时向任务发出信号。

因此,在您的情况下,仅使用 Task.Run 包装调用不会暴露可扩展性,而是会卸载。但是,该库的用户并不知道这一点。您的库的用户可以自己用 Task.Run 包装该调用。我真的认为他必须这么做。

关于c# - Task.Run 或 TaskFactory.StartNew 是否始终不适合在异步方法中使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59521005/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com