[英]How to execute multiple parallel tasks on completion of a prior task
我遇到一种情况,需要调用Web服务,并在成功完成后使用从Web服务返回的结果执行多项操作。 我已经开发了“有效”的代码-只是没有达到我的预期。 具体来说,我想从对Web服务的调用中获取结果,并将这些结果传递给多个要并行执行的连续任务,但是我现在拥有的是在执行第二个任务之前执行第一个连续任务。
我整理了一个有关当前正在执行的简化示例,希望可以帮助说明这种情况。 一,执行:
public interface IConfigurationSettings
{
int? ConfigurationSetting { get; set; }
}
public interface IPrintCommandHandler
{
System.Threading.Tasks.Task<bool> ExecuteAsync(byte[] reportContent);
}
public interface ISaveCommandHandler
{
System.Threading.Tasks.Task<bool> ExecuteAsync(byte[] reportContent);
}
public interface IWebService
{
System.Threading.Tasks.Task<object> RetrieveReportAsync(string searchToken, string reportFormat);
}
public class ReportCommandHandler
{
private readonly IConfigurationSettings _configurationSettings;
private readonly IPrintCommandHandler _printCommandHandler;
private readonly ISaveCommandHandler _saveCommandHandler;
private readonly IWebService _webService;
public ReportCommandHandler(IWebService webService, IPrintCommandHandler printCommandHandler, ISaveCommandHandler saveCommandHandler, IConfigurationSettings configurationSettings)
{
_webService = webService;
_printCommandHandler = printCommandHandler;
_saveCommandHandler = saveCommandHandler;
_configurationSettings = configurationSettings;
}
public async Task<bool> ExecuteAsync(string searchToken)
{
var reportTask = _webService.RetrieveReportAsync(searchToken, "PDF");
var nextStepTasks = new List<Task<bool>>();
// Run "print" task after report task.
var printTask = await reportTask.ContinueWith(task => _printCommandHandler.ExecuteAsync((byte[]) task.Result));
nextStepTasks.Add(printTask);
// Run "save" task after report task.
if (_configurationSettings.ConfigurationSetting.HasValue)
{
var saveTask = await reportTask.ContinueWith(task => _saveCommandHandler.ExecuteAsync((byte[]) task.Result));
nextStepTasks.Add(saveTask);
}
var reportTaskResult = await Task.WhenAll(nextStepTasks);
return reportTaskResult.Aggregate(true, (current, result) => current & result);
}
}
因此,Web服务(第三方,与我无关)具有用于执行搜索/查找的端点,如果成功,该端点将返回参考号(在我的示例中,我将其称为搜索令牌)。 然后,使用该参考号以几种不同格式中的任何一种来检索查找结果(使用不同的端点)。
在此示例中, IWebService
接口表示我创建的用于管理与Web服务的交互的应用程序服务。 实际的实现还有其他方法可以进行查找,ping等操作。
只是为了使事情变得有趣,需要一个连续的任务(将始终在主要任务之后执行),但是另一个连续的任务是可选的,执行取决于应用程序中其他位置的配置设置。
为了更容易地演示该问题,我创建了一个单元测试:
public class RhinoMockRepository : IDisposable
{
private readonly ArrayList _mockObjectRepository;
public RhinoMockRepository()
{
_mockObjectRepository = new ArrayList();
}
public T CreateMock<T>() where T : class
{
var mock = MockRepository.GenerateMock<T>();
_mockObjectRepository.Add(mock);
return mock;
}
public T CreateStub<T>() where T : class
{
return MockRepository.GenerateStub<T>();
}
public void Dispose()
{
foreach (var obj in _mockObjectRepository) obj.VerifyAllExpectations();
_mockObjectRepository.Clear();
}
}
[TestFixture]
public class TapTest
{
private const string SearchToken = "F71C8B50-ECD1-4C02-AD3F-6C24F1AF3D9A";
[Test]
public void ReportCommandExecutesPrintAndSave()
{
using (var repository = new RhinoMockRepository())
{
// Arrange
const string reportContent = "This is a PDF file.";
var reportContentBytes = System.Text.Encoding.Default.GetBytes(reportContent);
var retrieveReportResult = System.Threading.Tasks.Task.FromResult<object>(reportContentBytes);
var webServiceMock = repository.CreateMock<IWebService>();
webServiceMock.Stub(x => x.RetrieveReportAsync(SearchToken, "PDF")).Return(retrieveReportResult);
var printCommandHandlerMock = repository.CreateMock<IPrintCommandHandler>();
var printResult = System.Threading.Tasks.Task.FromResult(true);
printCommandHandlerMock
.Expect(x => x.ExecuteAsync(reportContentBytes))
//.WhenCalled(method => System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2)))
.Return(printResult);
var configurationSettingsStub = repository.CreateStub<IConfigurationSettings>();
configurationSettingsStub.ConfigurationSetting = 10;
var saveCommandHandlerMock = repository.CreateMock<ISaveCommandHandler>();
var saveResult = System.Threading.Tasks.Task.FromResult(true);
saveCommandHandlerMock.Expect(x => x.ExecuteAsync(reportContentBytes)).Return(saveResult);
// Act
var reportCommandHandler = new ReportCommandHandler(webServiceMock, printCommandHandlerMock, saveCommandHandlerMock, configurationSettingsStub);
var result = System.Threading.Tasks.Task
.Run(async () => await reportCommandHandler.ExecuteAsync(SearchToken))
.Result;
// Assert
Assert.That(result, Is.True);
}
}
}
理想情况下,在IWebService
上完成对RetrieveReportAsync()
的调用后,应该同时执行“打印”和“保存”命令处理程序,并从RetrieveReportAsync()
接收到结果的副本。 但是,如果未注释单元测试中对WhenCalled
...的调用,并且逐步执行ReportCommandHandler.ExecuteAsync()
,则可以看到“ print”命令在到达“保存”之前已执行并完成。命令。 现在,我知道await
的全部要点是暂停执行async
方法的调用,直到await
代码完成为止,但是我不清楚如何同时实例化“打印”和“保存”命令(任务)作为“ report”任务的延续,以便它们在“ report”任务完成时都并行执行,然后“ report”命令可以返回基于“ print”和“ result”结果的结果“保存”命令(任务)。
您的问题确实涉及解决两个不同的目标:
我发现在代码中混淆了await
和ContinueWith()
。 我不清楚您为什么这么做。 await
您完成的关键任务之一是自动设置一个延续,因此您不必显式调用ContinueWith()
。 但是,您还是要这样做。
出于对错误的认识,由于对如何实现目标缺乏全面的了解,因此我将按照以下方式编写您的方法:
public async Task<bool> ExecuteAsync(string searchToken)
{
var reportTaskResult = await _webService.RetrieveReportAsync(searchToken, "PDF");
var nextStepTasks = new List<Task<bool>>();
// Run "print" task after report task.
var printTask = _printCommandHandler.ExecuteAsync((byte[]) reportTaskResult);
nextStepTasks.Add(printTask);
// Run "save" task after report task.
if (_configurationSettings.ConfigurationSetting.HasValue)
{
var saveTask = _saveCommandHandler.ExecuteAsync((byte[]) reportTaskResult);
nextStepTasks.Add(saveTask);
}
var reportTaskResult = await Task.WhenAll(nextStepTasks);
return reportTaskResult.Aggregate(false, (current, result) => current | result);
}
换句话说,请先await
原始任务。 然后,您知道它已完成并产生了结果。 那时,请继续并启动其他任务,将其Task
对象添加到列表中,但不要单独等待每个Task
。 最后, await
任务的完整列表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.