C#任务ContinueWith不按预期工作

Big*_*ddy -1 c# asp.net-mvc task-parallel-library async-await

在继续执行之前如何等待上一个方法完成?我觉得这很容易,但事实并非如此.即使我已经阅读了很多例子,我也必须做一些非常愚蠢的事情.在下面的代码中,我不能让GetDocVM()方法执行,直到AddUserDocuments()方法完成.为什么?因为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
Run Code Online (Sandbox Code Playgroud)

Ada*_*mon 6

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

其中一个最大的问题是你不应该在ASP.NET上使用Task.Run来运行CPU限制的工作.这就是斯蒂芬克利里写的:

ASP.NET上的异步和等待都是关于I/O的.他们擅长阅读和编写文件,数据库记录和REST API.但是,它们不适合CPU绑定任务.您可以通过等待Task.Run开始一些后台工作,但这样做没有意义.事实上,这实际上会通过干扰ASP.NET线程池启发式来损害您的可伸缩性.如果你有关于ASP.NET的CPU限制工作,最好的办法是直接在请求线程上执行它.作为一般规则,不要将工作排队到ASP.NET上的线程池.

(我建议阅读他的文章,因为它是异步/等待知识的绝佳来源.)

所以你的代码清理了:

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

但是,我怀疑_genericUploadService.AddUserDocumentsGetDocVM做了一些与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);
Run Code Online (Sandbox Code Playgroud)