如何在完成先前任务时执行多个并行任务

时间:2017-03-29 23:39:46

标签: c# .net multithreading asynchronous parallel-processing

我遇到需要调用Web服务的情况,并且在成功完成后,使用Web服务返回的结果执行多项操作。我已经开发了“#34;工作" - 就像我没想的那样。具体来说,我想从调用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);
        }
    }
}

理想情况下,完成对RetrieveReportAsync() IWebService的调用后,&#34;打印&#34;并且&#34;保存&#34;在收到RetrieveReportAsync()的结果副本后,应同时执行命令处理程序。但是,如果单元测试中对WhenCalled ...的调用已取消注释,并且逐步执行ReportCommandHandler.ExecuteAsync(),则可以看到&#34; print&#34;命令在到达&#34; save&#34;之前执行并完成。命令。现在,我知道await的重点是暂停执行调用async方法,直到await ed代码完成,但我不清楚如何实例化&#34; print&#34;并且&#34;保存&#34;命令(任务)作为&#34;报告的延续&#34;任务使得它们在&#34;报告&#34;任务完成,&#34;报告&#34;然后命令可以返回基于&#34; print&#34;的结果的结果。并且&#34;保存&#34;命令(任务)。

1 个答案:

答案 0 :(得分:2)

您的问题实际上涉及两个不同的目标:

  1. 如何等待任务?
  2. 如何同时执行其他两项任务?
  3. 我发现代码中import LinearGradient from 'react-native-linear-gradient' 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整个任务列表。