Есть ли способ объединить несколько объектов 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.
Другие вопросы по тегам