【问题标题】:C# Task ContinueWith Not Working as ExpectedC# 任务 ContinueWith 未按预期工作
【发布时间】:2018-09-22 22:41:27
【问题描述】:

如何等到前一个方法完成后再继续执行?我以为这很容易,但事实并非如此。尽管我已经阅读了很多示例,但我一定是在做一些非常愚蠢的事情。在下面的代码中,我不能让 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

【问题讨论】:

  • 我想知道为什么您使用任务来运行不能同时运行的东西。你只是创造了很多开销而没有任何收获。
  • 我似乎无法重现您所看到的,但为什么首先要ContinueWith?只需在viewModel = GetDocVM 之后 await 就可以满足您的需求。当你有async/await 时,不需要ContinueWith
  • ContinueWith 是多余的。 await taskReturnsVoid 已经将该行之后的所有内容安排为任务完成后运行的延续。因此,通过将 viewModel = GetDocVM() 放在 await 之后,它就是已经安排好的延续的一部分。
  • @BigDaddy ContinueWith 工作得很好,但在这里是多余的。 await Task.Run 也很浪费。为什么要启动 another 线程只是为了阻止当前线程?您最终会导致额外的延迟,而不是在后台运行任何东西。 Web 请求已经由单独的线程提供服务
  • @BigDaddy 为什么使用 ContinueWith 而不是 await taskReturnsVoid ;var viewModel = await Task.Run(()=&gt;GetDocVM()); ?您是否遇到GetDocVM() 的问题并认为ContinueWith 会有所作为?

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


【解决方案1】:

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

最大的问题之一是您不应该在 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.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);

【讨论】:

  • 很好的回应,但没有解决为什么 ContinueWith 的行为不像我预期的那样。我的期望有缺陷吗?
  • Awaiting ContinueWith 的行为必须符合您的预期。我不知道你怎么观察到它没有。您可以通过运行this sample code 自己验证这一点。运行并等待 5 秒。
  • 代码库是同步和异步的混合体。我做了所有这些调用“一直异步”,现在它可以按照您描述的那样工作。我从未使用过 ContinueWith,所以这是一次学习经历。
  • 好的,很高兴能帮上忙。
猜你喜欢
  • 1970-01-01
  • 2018-03-10
  • 1970-01-01
  • 2021-07-10
  • 2017-10-21
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多