C#任务ContinueWith无法按预期工作

时间:2018-09-22 13:21:06

标签: c# asp.net-mvc async-await task-parallel-library

在继续执行之前,如何等待上一个方法完成?我以为这很容易,但事实并非如此。即使我已经阅读了很多示例,我也必须做一些非常愚蠢的事情。在下面的代码中,在 AddUserDocuments()方法完成之前,我不允许执行 GetDocVM()方法。为什么?因为 GetDocVM()不会提取刚刚添加的记录。我继承了此代码,并试图对其进行改进。

ut.ModelJSON = await Task.Run(() => _userTransactionService.ConvertToModelJson(typeof(UserDocument).Name, "", transactionDocs)).ConfigureAwait(false);
var taskReturnsVoid = Task.Run(() => _genericUploadService.AddUserDocuments(ut, docs));
List<GenericUploadDocumentViewModel> viewModel = new List<GenericUploadDocumentViewModel>();
await taskReturnsVoid.ContinueWith((t) =>
           {
                 viewModel = GetDocVM();//I EXPECTED THIS TO WAIT TO BE EXECUTED
           });
return Json(viewModel, JsonRequestBehavior.AllowGet);  //GETTING HERE TOO SOON

1 个答案:

答案 0 :(得分:6)

我不羡慕您,因为这看起来是一个非常糟糕的代码库,只是这几行遇到了多个问题。

最大的问题之一是,您不应该使用ASP.NET上的 Task.Run 来运行CPU限制的工作。这是what Stephen Cleary writes about this

  

在ASP.NET上异步和等待都是关于I / O的。他们确实擅长读写文件,数据库记录和REST API。但是,它们不适用于CPU密集型任务。您可以通过等待Task.Run开始一些后台工作,但是这样做没有任何意义。实际上,这实际上会干扰ASP.NET线程池启发式方法,从而损害您的可伸缩性。如果在ASP.NET上有CPU限制的工作要做,那么最好的选择就是直接在请求线程上执行它。通常,不要将工作排队到ASP.NET上的线程池中。

(我建议您阅读his articles,因为它是异步/等待知识的极好来源。)

因此您的代码被清除了:

ut.ModelJSON = _userTransactionService.ConvertToModelJson(typeof(UserDocument).Name, "", transactionDocs);
_genericUploadService.AddUserDocuments(ut, docs);
List<GenericUploadDocumentViewModel> viewModel = GetDocVM();
return Json(viewModel, JsonRequestBehavior.AllowGet);

但是,我怀疑 _genericUploadService.AddUserDocuments GetDocVM 会执行一些与I / O相关的工作(例如联网或数据库访问)。如果您想提高代码的性能,则应考虑将它们重写为异步方式,那么您可以这样做:

ut.ModelJSON = _userTransactionService.ConvertToModelJson(typeof(UserDocument).Name, "", transactionDocs);
await _genericUploadService.AddUserDocumentsAsync(ut, docs);
List<GenericUploadDocumentViewModel> viewModel = await GetDocVMAsync();
return Json(viewModel, JsonRequestBehavior.AllowGet);