异步操作中的依赖和执行顺序

时间:2014-08-19 13:48:12

标签: c# asynchronous task-parallel-library async-await

我正在开发一个执行远程安装各种软件组件的应用程序。跨机器存在一些组件安装顺序依赖关系,这些组件安装都是异步执行的。

我想知道建立这些组件安装顺序依赖项的最佳方法是什么。例如,我有我称之为"目标机器" A和B,以及组件1和2.这里,在机器B上安装组件2必须等待机器A上的组件1的完成。这是一个非常简单的示例,但我想建立一个框架,以便更复杂的方案可以很容易实现。

我目前的逻辑如下:

foreach (var tm in TargetMachines)
{
    IProgress<InstallProgress> p = tm.Progress;

    // Get installer module for this target machine
    var installModule =
        ServiceLocator.Default.GetAllInstances<IInstallModule>()
            .FirstOrDefault(m => m.ProductFamily.Equals(GetCurrentProductFamily()));

    // Add the Install() task to the queue
    if (installModule != null)
        installationTasks.Add(installModule.Install(tm.MachineName, p));
}

在这里,我只是为每个目标计算机迭代每个可用的installModule并运行其Install()方法。我的具体InstallModule实现负责以规定的(当前硬编码的)顺序调用Component安装。

您可以考虑两个并发时间轴:

[Target Machine A] === Component 1 ===> Finished

[Target Machine B] === Component 2 [Wait on Target Machine A, Component 1] ===> Finished

从这篇文章的标题中可以看出,我在Dynamics CRM的docs中看到了类似的功能,但看起来不合适的.NET BCL。

我知道我可以选择使用ContinueWith,这样我就可以taskA.ContinueWith(taskB)。但是,我主要关注如何表示这些依赖关系,以便我可以从中构建任务链。

我还发现this文章实现了DependencyManager,但早于TPL。五年后不确定这是否是最佳实践解决方案。

编辑1 :来自Microsoft的潜在解决方案here,&#34;任务图模式&#34;。

编辑2 :根据svick的建议,我正在尝试这个:

public class Component
{
    public Component()
    {
        Dependencies = new List<Component>();
    }

    public IEnumerable<Component> Dependencies { get; set; }

    public Task<ExecutionResult> InstallationCompletion { get; set; }

    public async Task<ExecutionResult> InstallAsync()
    {
        // http://stackoverflow.com/questions/25385129/dependency-and-execution-order-in-asynchronous-operations

        await Task.WhenAll(Dependencies.Select(c => c.InstallationCompletion));

        // install this component here

        var executionResult = new ExecutionResult(0, "Installation completed");
        return executionResult;
    }
}

1 个答案:

答案 0 :(得分:3)

我要做的是让每个组件包含它所依赖的组件列表,并公开Task,表明该组件的安装已完成。

然后您可以执行以下操作:

// there is no non-generic TaskCompletionSource, so use bool
private TaskCompletionSource<bool> installationCompletion
    = new TaskCompletionSource<bool>();

public Task InstallationCompletion { get { return installationCompletion.Task; } }

public async Task InstallAsync()
{
    await Task.WhenAll(this.Dependencies.Select(c => c.InstallationCompletion));

    // install this component here

    installationCompletion.SetResult(true);
}

(可能还使用继承将公共代码保留在一个地方。)

然后您可以同时开始安装所有组件,由于上面的代码,依赖关系将自行处理。

await Task.WhenAll(allComponents.Select(c => c.InstallAsync()));

如果您无法使用C#5.0,则可以将Task.Factory.ContinueWhenAll()用于与await Task.WhenAll()相同的目的,但语法较差。