我有一個呼叫服務方法的控制器,該方法呼叫同步存盤程序
服務:
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;
}
}
控制器:
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);
}
}
該程序可能需要很長時間(在某些情況下從 30 秒到 1 小時)。
問題是同步呼叫凍結了應用程式,因此我們使用 Task.Run。有人要求我不要在控制器中初始化執行緒。
現在我想知道什么是最好的實作,以免凍結應用程式并處理可能需要數小時才能完成的程序。
我真的應該為此創建一個執行緒嗎?
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;
});
}
而控制器是
[HttpPost]
public async Task<CustomResult> ProcessSomethingInDatabase(Criteria criteria){
List<SqlParameter> sqlParameters = CriteriaToSqlParams(criteria);
int result = await _serviceSomething.ProcessSomethingInDatabaseAsync(sqlParameters);
return new CustomResult(result);
}
還是應該使用 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);
}
注意:該服務托管在 Windows 服務上,并通過 WCF 進行通信
uj5u.com熱心網友回復:
你的問題的簡短答案是沒有一個,讓我們看看為什么。
您嘗試設計解決方案的方式至少存在一些問題。
首先,您聲稱您嘗試實施的操作可能需要長達 1 小時的處理時間。這意味著您不得在 HTTP 請求的背景關系中執行該操作。HTTP 請求是快速的,任何可能需要超過幾秒鐘時間的操作都不應該通過 HTTP 實作。Web 客戶端、Web 服務器和 Web 基礎設施都是為快速處理 HTTP 請求而設計的,并且到處都有超時,這不允許您在 HTTP 請求內部執行操作。
您可以使用 HTTP 請求要求后端執行長時間運行的操作。您的網路堆疊將處理請求并決定您請求的任務是否可以啟動(根據您的業務規則),但僅此而已:任務的實際執行必須委托給后端服務(例如通過使用佇列)。
這是一個很大的話題,但我希望你能明白:你不能使用 action 方法來執行長時間運行的操作;您的操作方法應該只驗證執行操作的請求,并將實際執行委托給其他人。您可以閱讀此博客文章以獲取有關此方法的更多資訊。
需要注意的第二點是Task.Run. 使用的唯一正當理由Task.Run是從 UI 執行緒呼叫受 CPU 限制的作業負載(例如,從 Windows 表單應用程式的事件處理程式呼叫長時間運行的受 CPU 限制的作業負載)。就是這樣。如果您處于除此之外的場景中,則應避免使用Task.Run. 如果您有一個本質上是異步的操作,例如資料庫查詢,您應該使用異步 api 來執行該操作,并await在一個async方法。在現代 .NET 代碼中完全支持異步 api,因此您始終可以使用它們來執行 IO 操作。如果您使用的資料訪問庫不提供異步 API,請更改您的資料訪問庫。
Task.Run在 ASP.NET 應用程式的背景關系中使用是特別危險的。在 ASP.NET 應用程式中,您有一個執行緒池,運行時使用這些執行緒池來處理傳入的 HTTP 請求。每次呼叫時,Task.Run您都在借用此執行緒之一來執行作業負載(由傳遞給 的委托表示Task.Run)。您不應該這樣做,因為執行緒是 Web 服務器中的重要資源,它們應該用于為傳入的 HTTP 請求提供服務并由 ASP.NET 運行時處理。通過借用一個執行緒來Task.Run執行您正在干擾 ASP.NET 執行緒池管理,您不應該這樣做。
總而言之,如果您正在撰寫 ASP.NET 代碼:
- 每次需要執行真正異步的作業負載時,請使用異步 API,例如資料庫查詢或對 Web 服務的 HTTP 請求。方法
await內部異步操作的結果,async并且永遠不會阻塞使用 apis 的異步操作,例如Task.Wait()和Task.Result - 如果您需要在操作方法內執行受 CPU 限制的同步作業負載,只需
do not使用Task.Run. 一般來說,永遠不要Task.Run在 ASP.NET 代碼中使用:任何有意義的地方是 UI 客戶端應用程式,例如 windows 表單或 WPF 應用程式。使用Task.Run每次你需要呼叫長從UI執行緒中運行的CPU系結的作業量時間,這樣你就不會凍結應用程式UI。 - 永遠不要執行在 HTTP 請求中可能持續超過幾秒鐘的操作。HTTP 請求旨在快速處理。使用佇列機制將長時間運行的任務的執行委托給后端服務。
考慮閱讀此博客文章,了解有關Task.Run.
關于Task.FromResult<T>,您在問題中提到的最后一點。此方法旨在創建一個Task實體,表示成功完成的異步操作,具有指定的結果。就是這樣。這不是偽造異步的方法(一般來說應該避免偽造異步),它只是解決Task為已經完成的操作創建實體產生一定結果的問題的一種方法。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/350337.html
標籤:C# asp.net 。网 asp.net-mvc
