Есть ли способ объединить несколько объектов IProgress<float>?
Я хотел бы объединить два объекта IProgress<T> в один IProgress<T>, где T — число. Он всегда должен возвращать среднее значение всех объединенных прогрессов. Например, Progress1 = 50% и Progress2 = 70% для mergedProgress = 60%.
Возможно, это могло бы выглядеть так, но пока я не нашел способа объединить два или более объектов IProgress:
IProgress<float> p1 = new Progress(value=> Console.WriteLine(value));
IProgress<float> p2 = new Progress(value=> Console.WriteLine(value));
IProgress<float> pM = Progress.Merge(p1,p2,value=> Console.WriteLine("Full progress: " + value));
До сих пор я нашел только один способ, который мне нужно использовать при создании объектов Progress для вывода отрывка в другом Progress. Я сохранил возвращаемые значения в массиве, а затем вычислил среднее значение вручную, поскольку метод «.Average()» не работает, поскольку к массиву обращаются несколько потоков:
float[] progress = new float[2]
IProgress<float> pM = new Progress(value=>
{
double p=0;
for (int i = 0; i < progress.Length; i++)
{
p+=progress[i]
}
Console.WriteLine("Full progress: " + p/progress.Length)
});
IProgress<float> p1 = new Progress(value=>{ progress[0]=value; pM.Report(0); });
IProgress<float> p2 = new Progress(value=>{ progress[1]=value; pM.Report(0); });
Есть ли способ объединить объекты Progress<float>?
1 ответ
Вы можете написать свой собственный смеситель:
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();
}
Это предполагает, что все источники прогресса используют одно и то же масштабирование. В противном случае вычисленное среднее значение, конечно, будет неверным.
Пример консольного приложения, использующего его (обратите внимание на добавлениеConsole.WriteLine()
кprogress_ProgressChanged()
что вам не нужно в производственном коде):
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();
}
Пример вывода:
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.