You could write your own amalgamator:
public sealed class ProgressAmalgamator : Progress<double>
{
public void Attach(Progress<double> progress)
{
lock (_lock)
{
_progressors.Add(progress);
_progress.Add(0);
progress.ProgressChanged += progress_ProgressChanged;
}
}
void progress_ProgressChanged(object? sender, double e)
{
double average = 0;
lock (_lock)
{
for (int i = 0; i < _progressors.Count; i++)
{
if (ReferenceEquals(_progressors[i], sender))
{
_progress[i] = e;
break;
}
}
average = _progress.Average();
}
OnReport(average);
}
readonly List<IProgress<double>> _progressors = new();
readonly List<double> _progress = new();
readonly object _lock = new object();
}
This assumes that all the progress sources are using the same scaling - if they don't, the calculated average will of course be incorrect.
Sample console app using it (note the addition of a Console.WriteLine()
to progress_ProgressChanged()
that you don't want in production code):
namespace Console1;
public static class Program
{
public static async Task Main()
{
var progress1 = new Progress<double>();
var progress2 = new Progress<double>();
var progress3 = new Progress<double>();
var amalgamator = new ProgressAmalgamator();
amalgamator.Attach(progress1);
amalgamator.Attach(progress2);
amalgamator.Attach(progress3);
amalgamator.ProgressChanged += (_, progress) => Console.WriteLine($"Amalgamated progress = {progress}");
Task[] tasks =
{
Task.Run(() => simulateProgress("A", progress1, 10, 1000)),
Task.Run(() => simulateProgress("B", progress2, 20, 2000)),
Task.Run(() => simulateProgress("C", progress3, 50, 5000))
};
await Task.WhenAll(tasks);
Console.WriteLine("All tasks completed.");
Console.ReadLine();
}
static void simulateProgress(string name, IProgress<double> progress, double step, int delay)
{
double current = 0;
while (current < 100)
{
Thread.Sleep(delay);
current += step;
Console.WriteLine($"Thread {name} is reporting progress = {current}");
progress.Report(Math.Min(current, 100));
}
}
}
public sealed class ProgressAmalgamator : Progress<double>
{
public void Attach(Progress<double> progress)
{
lock (_lock)
{
_progressors.Add(progress);
_progress.Add(0);
progress.ProgressChanged += progress_ProgressChanged;
}
}
void progress_ProgressChanged(object? sender, double e)
{
double average = 0;
lock (_lock)
{
for (int i = 0; i < _progressors.Count; i++)
{
if (ReferenceEquals(_progressors[i], sender))
{
Console.WriteLine($"Setting progress for progressor {i} to {e}");
_progress[i] = e;
break;
}
}
average = _progress.Average();
}
OnReport(average);
}
readonly List<IProgress<double>> _progressors = new();
readonly List<double> _progress = new();
readonly object _lock = new object();
}
Sample output:
Thread A is reporting progress = 10
Setting progress for progressor 0 to 10
Amalgamated progress = 3.3333333333333335
Thread A is reporting progress = 20
Thread B is reporting progress = 20
Setting progress for progressor 0 to 20
Setting progress for progressor 1 to 20
Amalgamated progress = 6.666666666666667
Amalgamated progress = 13.333333333333334
Thread A is reporting progress = 30
Setting progress for progressor 0 to 30
Amalgamated progress = 16.666666666666668
Thread A is reporting progress = 40
Thread B is reporting progress = 40
Setting progress for progressor 0 to 40
Amalgamated progress = 20
Setting progress for progressor 1 to 40
Amalgamated progress = 26.666666666666668
Thread C is reporting progress = 50
Setting progress for progressor 2 to 50
Amalgamated progress = 43.333333333333336
Thread A is reporting progress = 50
Setting progress for progressor 0 to 50
Amalgamated progress = 46.666666666666664
Thread B is reporting progress = 60
Setting progress for progressor 1 to 60
Amalgamated progress = 53.333333333333336
Thread A is reporting progress = 60
Setting progress for progressor 0 to 60
Amalgamated progress = 56.666666666666664
Thread A is reporting progress = 70
Setting progress for progressor 0 to 70
Amalgamated progress = 60
Thread B is reporting progress = 80
Setting progress for progressor 1 to 80
Amalgamated progress = 66.66666666666667
Thread A is reporting progress = 80
Setting progress for progressor 0 to 80
Amalgamated progress = 70
Thread A is reporting progress = 90
Setting progress for progressor 0 to 90
Amalgamated progress = 73.33333333333333
Thread C is reporting progress = 100
Setting progress for progressor 2 to 100
Amalgamated progress = 90
Thread B is reporting progress = 100
Setting progress for progressor 1 to 100
Amalgamated progress = 96.66666666666667
Thread A is reporting progress = 100
Setting progress for progressor 0 to 100
Amalgamated progress = 100
All tasks completed.