Reputation: 29710
I am working on an application which performs remote installation of various software components. There exist some component installation order dependencies across machines, and these component installs are all performed asynchronously.
I'm wondering what the best approach is to establishing these component installation order dependencies. For example, I have what I call "Target Machines" A and B, and Components 1 and 2. Here, installing Component 2 on Machine B must wait for the completion of Component 1 on Machine A. This is a very simple example, but I want to establish a framework so that more complex scenarios can be easily implemented.
My current logic is as follows:
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));
}
Here, I'm simply iterating through each available installModule
for each Target Machine and running its Install()
method. My concrete InstallModule
implementations are responsible for invoking the Component installation in a prescribed (currently hardcoded) order.
You can think of two concurrent timelines:
[Target Machine A] === Component 1 ===> Finished
[Target Machine B] === Component 2 [Wait on Target Machine A, Component 1] ===> Finished
As evidenced from this post's title, I saw a similar feature in Dynamics CRM's docs, but nothing the .NET BCL which looks appropriate.
I understand that I have the option to use ContinueWith
, such that I can do taskA.ContinueWith(taskB)
. However, I am primarily interested in how to represent these dependencies in such a way that I can construct the task chain from it.
I also found this article which implements a DependencyManager
but predates the TPL. Not sure if this is a best-practices solution five years hence.
EDIT 1: Potential solution here from Microsoft, the "Task Graph Pattern".
EDIT 2: Per svick's suggestion, I am experimenting with this:
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;
}
}
Upvotes: 2
Views: 1035
Reputation: 244787
What I would do is to make each component contain a list of components that it depends on and also expose a Task
indicating that installation of that component completed.
You can then do something like:
// 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);
}
(Possibly also using inheritance to keep the common code in one place only.)
Then you can start the installation of all components at the same time, the dependencies will take care of themselves thanks to the above code.
await Task.WhenAll(allComponents.Select(c => c.InstallAsync()));
If you can't use C# 5.0, you can use Task.Factory.ContinueWhenAll()
for the same purpose as await Task.WhenAll()
, only with worse syntax.
Upvotes: 3