Mik*_*oud 5 c# asynchronous async-await asp.net-mvc-5
我有一个导入操作,我想在另一个线程上执行,希望UI立即响应.所以,我开始沿着这条路走下去并创建了一个这样的动作:
[HttpPost]
public async Task<RedirectToRouteResult> ImportAsync(HttpPostedFileBase file)
{
var importsRoot = Server.MapPath("~/App_Data/Imports");
var path = Path.ChangeExtension(Path.Combine(importsRoot, Guid.NewGuid().ToString()), "txt");
if (!Directory.Exists(importsRoot))
{
Directory.CreateDirectory(importsRoot);
}
file.SaveAs(path);
// start the import process
await ImportManager.Instance.StartImport(User.Identity.Name);
return RedirectToAction("Index");
}
Run Code Online (Sandbox Code Playgroud)
在ImportManager
现在有两个目的:
该StartImport
方法如下所示:
public async Task StartImport(string user)
{
string[] files;
var importRoot = HttpContext.Current.Server.MapPath("~/App_Data/Imports");
var processingRoot = HttpContext.Current.Server.MapPath("~/App_Data/Processing");
var processedRoot = HttpContext.Current.Server.MapPath("~/App_Data/Processed");
lock (lockObj)
{
// make sure the "Processing" folder exists
if (!Directory.Exists(processingRoot))
{
Directory.CreateDirectory(processingRoot);
}
// find all of the files available and move them to the "Processing" folder
files = Directory.GetFiles(importRoot);
foreach (var file in files)
{
var fileName = Path.GetFileName(file);
if (fileName == null)
{
continue;
}
File.Move(file, Path.Combine(processingRoot, fileName));
}
// make sure the "Processed" directory exists
if (!Directory.Exists(processedRoot))
{
Directory.CreateDirectory(processedRoot);
}
}
await Task.Run(() =>
{
// start processing the files
foreach (var file in files)
{
var fileName = Path.GetFileName(file);
if (fileName == null)
{
continue;
}
var processingFileName = Path.Combine(processingRoot, fileName);
var processedFileName = Path.Combine(processedRoot, fileName);
var recognizer = new Recognizer(processingFileName);
recognizer.ProgressChanged += (s, e) => Clients.All.updateImportStatus(e.ProgressPercentage, user);
recognizer.Recognize(DataManager.GetExclusionPatterns());
// move the file to the "Processed" folder
File.Move(processingFileName, processedFileName);
}
Clients.All.importComplete();
});
}
Run Code Online (Sandbox Code Playgroud)
调试我们发现,当我点击await Task.Run(() =>
它同步运行(一段时间),因为UI没有得到重定向的请求,Index
直到说已读取30K +行.
我怎样才能让它简单地执行并忘记它?我需要使用不同的方法吗?
Mar*_*ell 13
它以异步方式运行; 但是你在等待它:
await ImportManager.Instance.StartImport(User.Identity.Name);
return RedirectToAction("Index");
Run Code Online (Sandbox Code Playgroud)
不等 等待.现在RedirectToAction
是一个继续,将在其他代码完成时调用.
如果你不想等待; 不要await
.但是,您应该考虑如果发生错误等会发生什么.async
如果没有人要观察它,请不要让您的方法引发异常:坏事.
我怎样才能让它简单地执行并忘记它?
我强烈建议你不要在ASP.NET上使用"fire and forget".核心原因是ASP.NET在请求生命周期内管理您的应用程序生命周期.因此,如果您执行的代码不是请求的一部分,那么ASP.NET可能会删除您的应用程序.在一般情况下,这意味着您不能依赖于实际执行的代码.
我需要使用不同的方法吗?
正确的解决方案是将工作放入可靠的队列(例如,Azure队列或MSMQ),并使独立的后端执行实际处理(例如,Azure webrole,Azure辅助角色或Win32服务).这是相当多的工作,但它是唯一可靠的解决方案.
但是,如果您希望危险地生活并将工作保留在ASP.NET进程的内存中,则应该使用ASP.NET运行时注册该工作.您仍然无法保证处理将执行,但更有可能,因为ASP.NET知道您的代码.我有一个NuGet包,可以帮到你; 只是用BackgroundTaskManager.Run
而不是Task.Run
.