C# 任务。运行isn';它不会像我想象的那样异步运行吗?
我想在另一个线程上执行一个导入操作,希望UI立即响应。因此,我开始沿着这条路走,并创建了一个如下的动作:C# 任务。运行isn';它不会像我想象的那样异步运行吗?,c#,asynchronous,async-await,asp.net-mvc-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
[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");
}
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();
});
}
发生了什么事?
调试我们发现,当我点击wait Task.Run(()=>
时,它会同步运行(至少有一段时间),因为在读取了30K+行之前,UI不会收到重定向到索引的请求
我怎样才能简单地执行并忘记它?我是否需要使用不同的方法?它正在异步运行;但您正在等待它:
未等待;正在等待。重定向到操作
现在是一个继续,将在其他代码完成时调用
如果您不想等待,请不要等待
。但是,您应该考虑发生错误时会发生什么,等等。如果没有人注意到,请不要让异步
方法引发异常:糟糕的事情
我怎样才能简单地执行并忘记它
我强烈建议你不要使用“开火然后忘记”在ASP.NET上。核心原因是ASP.NET围绕请求生命周期管理您的应用程序生命周期。因此,如果您执行的代码不是请求的一部分,则ASP.NET可能会关闭您的应用程序。在一般情况下,这意味着您不能依赖实际执行的代码
我需要使用不同的方法吗
正确的解决方案是将工作放入一个可靠的队列(例如Azure队列或MSMQ),并让一个独立的后端执行实际的处理(例如Azure webrole、Azure worker role或Win32 service)。这是相当多的工作,但这是唯一可靠的解决方案
但是,如果您想生活在危险之中,并将工作保存在ASP.NET进程的内存中,您应该在ASP.NET运行时注册该工作。您仍然不能保证该进程将执行,但更可能的是,因为ASP.NET知道您的代码。我有一个。所以不要等待StartImport
只要执行它,RedirectToAction
将是即时响应?@MichaelPerrenoud不,它将在该点创建一个延续,在wait
之后的代码将是该延续的一部分,并将在异步任务完成时执行。@MichaelPerrenoud确实如此;我添加了最后一行来解释这一点,但有一个重要的警告exceptions@MarcGravell,太好了!我擅长处理异常,因为我会在处理过程中默默地处理这些异常,然后手动处理它们。@AdamHouldsworth它只是一个延续,因为wait
。如果OP删除wait
,它就不再是一个延续。事实上,如果没有await
操作也不需要是async
方法。
await ImportManager.Instance.StartImport(User.Identity.Name);
return RedirectToAction("Index");