Отчет о прогрессе в консольном приложении с использованием Parallel.ForEach - PullRequest
0 голосов
/ 02 декабря 2018

У меня есть следующий код (ниже), который в основном работает, но я не получаю ожидаемый результат.Я пытаюсь вывести на консоль строку для каждого обрабатываемого элемента.Я хочу показать процент выполнения по мере выполнения задач.

Это вывод, который я получаю (первые несколько строк повторяются).Я не ожидаю, что они повторятся, поэтому я знаю, что у меня что-то не так.Независимо от того, сколько раз я запускаю это, первые несколько строк одинаковы.Можно ли заставить вывод отображать 1 -> 10%, 2 -> 20% и т. Д.

2 --> 20 %  -- Bytes Read = 23971
2 --> 20 %  -- Bytes Read = 23971
2 --> 20 %  -- Bytes Read = 23971
3 --> 30 %  -- Bytes Read = 35909
4 --> 40 %  -- Bytes Read = 47872
5 --> 50 %  -- Bytes Read = 59824
6 --> 60 %  -- Bytes Read = 71775
7 --> 70 %  -- Bytes Read = 83746
8 --> 80 %  -- Bytes Read = 95701
9 --> 90 %  -- Bytes Read = 107686
Elapsed Time: : 702
Press Any Key

Вот код, который я запускаю

using RestSharp;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace RestSharpAsyncTest
{
    public class SomeItem
    {
        public int Id { get; set; }
        public long BytesRead { get; set; }

        public SomeItem(int i)
        {
            this.Id = i;
            this.BytesRead = 0;
        }

        public long ProcessItem()
        {
            var client = new RestClient("http://www.google.com");
            var request = new RestRequest("", Method.GET);

            var restResponse = client.Execute(request);

            return restResponse.RawBytes.Length;
        }
    }

    public class ProgressReport
    {
        public int Counter { get; set; } = 0;
        public long TotalBytesRead { get; set; }
        public int PercentComplete { get; set; }
    }

    class Program
    {
        private static async Task DoSomethingAsync(List<SomeItem> someItems, IProgress<ProgressReport> progress)
        {
            /// Set up the local progress reporter
            ProgressReport progressReport = new ProgressReport();

            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();

            await Task.Run(() =>
            {
                Parallel.ForEach(someItems, someItem =>
                {
                    long bytesRead = someItem.ProcessItem();
                    progressReport.Counter++;
                    progressReport.PercentComplete = (progressReport.Counter * 100) / someItems.Count;
                    progressReport.TotalBytesRead += bytesRead;
                    progress.Report(progressReport);                    
                });
            });

            watch.Stop();

            Console.WriteLine("Elapsed Time: : {0}", watch.ElapsedMilliseconds);
        }

        static async Task Main(string[] args)
        {
            Progress<ProgressReport> pr = new Progress<ProgressReport>();
            pr.ProgressChanged += Pr_ProgressChanged;

            // Create some items
            List<SomeItem> someItems = new List<SomeItem>();

            for (int i = 1; i < 11; i++)
                someItems.Add(new SomeItem(i));

            await DoSomethingAsync(someItems, pr);

            Console.WriteLine("Press Any Key");
            Console.ReadKey();
        }

        private static void Pr_ProgressChanged(object sender, ProgressReport e)
        {
            Console.WriteLine($"{e.Counter } --> { e.PercentComplete } %  -- Bytes Read = { e.TotalBytesRead }");
        }
    }
}

РЕДАКТИРОВАТЬ: я добавил следующее, но все равно получаю те же результаты.

public class Program
{
    private static object locker = new object();

затем

lock (locker)
{
    progressReport.Counter++;
    progressReport.PercentComplete = (progressReport.Counter * 100) / someItems.Count;
    progressReport.TotalBytesRead += bytesRead;
    progress.Report(progressReport);
}

1 Ответ

0 голосов
/ 02 декабря 2018

Я вижу много проблем с вашим кодом.Это довольно странно.

Вы не должны использовать Task.Run, вы должны просто использовать Parallel.ForEach (), а затем ждать в ProcessItem, который должен быть переписан, чтобы быть асинхронным методом, используя остальныеклиенты асинхронные методы.

Кроме того, у вас есть большое неправильное представление о том, как работает интерфейс IProgress.Это не синхронно.Это асинхронно.Когда вы вызываете Report (), он фактически помещает элемент очереди в пул потоков для вызова обработчика событий.Поскольку вы используете общий объект, он будет обновлен к моменту печати. ​​

IProgress НЕ ДОЛЖЕН использовать общие объекты для создания отчетов в многопоточной среде!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...