[英]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.