簡體   English   中英

如何在完成先前任務時執行多個並行任務

[英]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”結果的結果“保存”命令(任務)。

您的問題確實涉及解決兩個不同的目標:

  1. 如何等待任務?
  2. 如何同時執行另外兩個任務?

我發現在代碼中混淆了awaitContinueWith() 我不清楚您為什么這么做。 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM