简体   繁体   English

我应该使用 Task.Run 还是 Task.FromResult?

[英]Should I use Task.Run or Task.FromResult?

I have a controller which calls a service method, this method calls synchronous a stored procedure我有一个调用服务方法的控制器,该方法调用同步存储过程

Service:服务:

public class ServiceSomething : IServiceSomething, IService {
   public int ProcessSomethingInDatabase(List<SqlParameter> sqlParameters){
      IConnection connection = ConnectionFactory.GetConnection();
      //This makes a synchronous call
      DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
      int result = GetResultFromDataTable(dataTable);
      return result;
   }  
}

Controller:控制器:

public class SomethingController : Controller {
   private readonly IServiceSomething _serviceSomething;

   [HttpPost]
   public async Task<CustomResult> ProcessSomethingInDatabase(Criteria criteria){
      List<SqlParameter> sqlParameters = CriteriaToSqlParams(criteria);
      int result = await Task.Run(() => _serviceSomething.ProcessSomethingInDatabase(sqlParameters));
      return new CustomResult(result);
   }
}

That process could take a long time (from 30 seconds to 1 hour on some occasions).该过程可能需要很长时间(在某些情况下从 30 秒到 1 小时)。

The problem is that the synchronous call freezes the application, and because of that we use Task.Run.问题是同步调用冻结了应用程序,因此我们使用 Task.Run。 It has been requested to me that the thread don't be initialized in the controller.有人要求我不要在控制器中初始化线程。

Now I would like to know what is the best implementation, so much as not to freeze the application and to handle a process that can take hours to finish.现在我想知道什么是最好的实现,以免冻结应用程序并处理可能需要数小时才能完成的过程。

Should I really create a thread for this?我真的应该为此创建一个线程吗?

public async Task<int> ProcessSomethingInDatabaseAsync(List<SqlParameter> sqlParameters){
       IConnection connection = ConnectionFactory.GetConnection();
       return await Task.Run(() => {
          DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
          int result = GetResultFromDataTable(dataTable);
          return result;
       });
}  

And the controller be而控制器是

[HttpPost]
public async Task<CustomResult> ProcessSomethingInDatabase(Criteria criteria){
   List<SqlParameter> sqlParameters = CriteriaToSqlParams(criteria);
   int result = await _serviceSomething.ProcessSomethingInDatabaseAsync(sqlParameters);
   return new CustomResult(result);
}

or should I use Task.FromResult?还是应该使用 Task.FromResult?

public Task<int> ProcessSomethingInDatabaseAsync(List<SqlParameter> sqlParameters){
       IConnection connection = ConnectionFactory.GetConnection();
       DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
       int result = GetResultFromDataTable(dataTable);
       return Task.FromResult(result);
}

Note: The service is hosted on a Windows Service and it is communicated through WCF注意:该服务托管在 Windows 服务上,并通过 WCF 进行通信

The short answer to your question is none of them , let's see why.你的问题的简短答案是没有一个,让我们看看为什么。

There are at least a couple of issues in the way you are trying to design your solution.您尝试设计解决方案的方式至少存在一些问题。

First of all you claimed that the operation you are trying to implement could take up until 1 hour of processing.首先,您声称您尝试实施的操作可能需要长达 1 小时的处理时间。 This means that you must not execute that operation in the context of an HTTP request.这意味着您不得在 HTTP 请求的上下文中执行该操作。 HTTP requests are meant to be quick, any operation that can take a time greater than a few seconds should not be implemented via HTTP. HTTP 请求是快速的,任何可能需要超过几秒钟时间的操作都不应该通过 HTTP 实现。 Web clients, web servers and web infrastructure are all designed for quick processing of HTTP requests and there are timeouts everywhere which won't allow you to perform your operation inside of an HTTP request. Web 客户端、Web 服务器和 Web 基础设施都是为快速处理 HTTP 请求而设计的,并且到处都有超时,这不允许您在 HTTP 请求内部执行操作。

You can use an HTTP request to ask your backend to perform a long running operation.您可以使用 HTTP 请求要求后端执行长时间运行的操作。 Your web stack will process the request and will decide whether or not the task that you are requesting can be started (based on your business rules), but that's it: the actual execution of the task must be delegated to backend services (for instance by using a queue).您的网络堆栈将处理请求并决定您请求的任务是否可以启动(根据您的业务规则),但仅此而已:任务的实际执行必须委托给后端服务(例如通过使用队列)。

This is a large topic, but I hope you get the idea: you can't use an action method to perform a long running operation;这是一个很大的话题,但我希望你能明白:你不能使用 action 方法来执行长时间运行的操作; your action method should only validate the request to execute the operation and delegate the actual execution to someone else.您的操作方法应该只验证执行操作的请求,并将实际执行委托给其他人。 You can read this blog post to get more information about this approach.您可以阅读此博客文章以获取有关此方法的更多信息。

The second point which needs attention is the usage of Task.Run .第二点需要注意的是Task.Run的用法。 The only valid reason to use Task.Run is calling CPU-bound workload from a UI thread (for instance calling a long running CPU-bound workload from an event handler of a windows form application) .使用Task.Run的唯一正当理由是从 UI 线程调用受 CPU 限制的工作负载(例如,从 Windows 窗体应用程序的事件处理程序调用长时间运行的受 CPU 限制的工作负载) That's it.就是这样。 If you are in a scenario other than this one you should avoid the usage of Task.Run .如果您处于Task.Run的场景中,则应避免使用Task.Run If you have an operation which is asynchronous in nature, such as a database query, you should use an asynchronous api to execute that operation and await the result inside of an async method.如果您有一个本质上是异步的操作,例如数据库查询,您应该使用异步 API 来执行该操作并await async方法中await结果。 In modern .NET code there is full support for asynchronous apis, so you can always use them to perform IO operations.在现代 .NET 代码中完全支持异步 api,因此您始终可以使用它们来执行 IO 操作。 If you are using a data access library which doesn't offer asynchronous apis, then change your data access library.如果您使用的数据访问库不提供异步 API,请更改您的数据访问库。

The usage of Task.Run is particularly dangerous in the context of ASP.NET applications. Task.Run的使用在 ASP.NET 应用程序的上下文中特别危险。 In ASP.NET applications you have a pool of threads which are used by the runtime to handle incoming HTTP requests.在 ASP.NET 应用程序中,您有一个线程池,运行时使用这些线程池来处理传入的 HTTP 请求。 Each time you call Task.Run you are borrowing one of this threads to execute a workload (represented by the delegate that you pass to Task.Run ).每次调用Task.Run您都是借用此线程之一来执行工作负载(由传递给Task.Run的委托表示)。 You are not expected to do that, because threads are an important resource in a web server and they should be used to serve incoming HTTP requests and handled by the ASP.NET runtime.您不应该这样做,因为线程是 Web 服务器中的重要资源,它们应该用于为传入的 HTTP 请求提供服务并由 ASP.NET 运行时处理。 By borrowing one threads for your Task.Run execution you are interfering with the ASP.NET thread pool management and you should not do that .通过为Task.Run执行借用一个线程,您正在干扰 ASP.NET 线程池管理,您不应该这样做

To summarize, if you are writing ASP.NET code:总而言之,如果您正在编写 ASP.NET 代码:

  • use asynchronous apis each time you need to perform a workload which is truly asynchronous, such as a database query or an HTTP request to a web service.每次需要执行真正异步的工作负载时,请使用异步 API,例如数据库查询或对 Web 服务的 HTTP 请求。 await the result of the asynchronous operation inside of an async method and never block on asynchronous operations using apis such as Task.Wait() and Task.Result await async方法中await异步操作的结果,并且永远不要使用诸如Task.Wait()Task.Result api 阻塞异步操作
  • if you need to perform a CPU-bound synchronous workload inside of an action method, simply do that and do not fake asynchrony by wrapping your method call by using Task.Run .如果您需要在操作方法内执行受 CPU 限制的同步工作负载,只需执行此操作, do not通过使用Task.Run包装您的方法调用来伪造异步。 Generally speaking never use Task.Run in ASP.NET code: the ony place where it makes sense is UI client applications, such as windows forms or WPF applications.一般来说,永远Task.Run在 ASP.NET 代码中使用Task.Run :任何有意义的地方是 UI 客户端应用程序,例如 windows 窗体或 WPF 应用程序。 Use Task.Run each time you need to call long running cpu-bound workload from a UI thread, so that you do not freeze the application UI.每次需要从 UI 线程调用长时间运行的 cpu 绑定工作负载时,请使用Task.Run ,这样您就不会冻结应用程序 UI。
  • never execute operations which can last more than a few seconds inside of HTTP requests.永远不要执行在 HTTP 请求中可能持续超过几秒钟的操作。 HTTP requests are meant to be processed quickly. HTTP 请求旨在快速处理。 Use a queue mechanism to delegate to backend services the execution of long running tasks.使用队列机制将长时间运行的任务的执行委托给后端服务。

Consider reading this blog post for more information about the usage of Task.Run .考虑阅读这篇博文,了解有关Task.Run用法的更多信息。

A final note on Task.FromResult<T> , that you mentioned in your question.关于Task.FromResult<T>最后一点,您在问题中提到了。 This method is meant to create a Task instance representing a successfully completed asynchronous operation , having a specified result.此方法旨在创建一个Task实例,表示成功完成的异步操作,具有指定的结果。 That's it.就是这样。 This is not a way to fake asynchrony (generally speaking you should avoid faking asynchrony), it's just a way to solve the problem of creating a Task instance for an already completed operation producing a certain result.这不是伪造异步的方法(一般来说你应该避免伪造异步),它只是解决为已经完成的操作创建一个Task实例产生一定结果的问题的一种方法。

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

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