N
N
Nubzilo2015-03-18 17:35:48
WPF
Nubzilo, 2015-03-18 17:35:48

How to properly inform about the progress of the task?

Good afternoon. How to correctly and beautifully notify about the progress of the task? For example notify progressbar and display message on textbox on winforms?
For example, such a class

class WorkClass
{
  public async Task LongMethod(List<string> somethink)
  {
   await Task.Factory.StartNew(() =>
   {
//отсюда делать вывод somethink.Count()
    foreach (string element in somethink)
    {
      Thread.Sleep(5000);
      //тут делать вывод о выполнении
    }
   });
  }
}

There is an option to do through events. But creating at least 3 events in order to inform and set up the progressbar seems to me not entirely correct.
I tried using the Progress class, but here I did not like to produce a large number of classes through
Progress.Report(new MyReport{...});
In general, how do smart people solve this problem?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
L
lam0x86, 2015-03-19
@Nubzilo

I tried using the Progress class, but here I did not like to produce a large number of classes through
Progress.Report(new MyReport{...});

There is nothing wrong with that - these objects actually die right there without leaving the first generation of GC.
Here another question is more important: the Progress class is designed in such a way that it marshalls all reports in the SynchronizationContext in which it was created. In the case of WinForms, this is the context of the UI thread. If your Task calls the Report method too often, this can negatively affect the UI thread, because it will be filled with reports, each of which it must process. Moreover, since the marshalling in the Progress class is asynchronous, there is a chance that the task will be completed before all the reports are processed on the UI thread. This will result in a delay of the progress bar from real progress (and this is where the likelihood of reports transitioning to Gen1 and Gen2 arises).
I would write my own implementation of the Progress class that uses a timer that periodically polls for the latest progress update. This is done on most systems where updates can happen frequently, such as when copying many files. More or less like this:
public interface IProgressInfo
    {
        bool IsCompleted { get; }
    }

    public class ProgressInfo : IProgressInfo
    {
        public ProgressInfo(double completedPercentage, string progressStatusText)
        {
            CompletedPercentage = completedPercentage;
            ProgressStatusText = progressStatusText;
        }

        public double CompletedPercentage { get; private set; }

        public string ProgressStatusText { get; private set; }

        public bool IsCompleted
        {
            get { return CompletedPercentage >= 1; }
        }
    }

    public class Progress<T> : IProgress<T> where T : class, IProgressInfo
    {
        private T _previousProgressInfo;
        private volatile T _progressInfo;
        private readonly Action<T> _updateProgressAction;
        private readonly Timer _timer;
        private readonly SynchronizationContext _synchronizationContext;

        public Progress(TimeSpan pollingInterval, Action<T> updateProgressAction)
        {
            _synchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext();
            _updateProgressAction = updateProgressAction;
            _timer = new Timer(TimerCallback, null, pollingInterval, pollingInterval);
        }

        private void TimerCallback(object state)
        {
            ProcessUpdate();
        }

        private void ProcessUpdate()
        {
            var progressInfo = _progressInfo;
            if (_previousProgressInfo != progressInfo)
            {
                _synchronizationContext.Send(state => _updateProgressAction((T) state), progressInfo);
            }
            _previousProgressInfo = progressInfo;
        }

        public void Report(T value)
        {
            _progressInfo = value;
            if (value.IsCompleted)
            {
                _timer.Dispose();
                ProcessUpdate();
            }
        }
    }

You can use it like this:
var workClass = new WorkClass();

            var list = Enumerable.Range(1, 1000).Select(i => i.ToString()).ToArray();
            var progress = new Progress<ProgressInfo>(TimeSpan.FromMilliseconds(100), UpdateProgressAction);

            await workClass.LongMethod(list, progress);

where:
private void UpdateProgressAction(ProgressInfo obj)
        {
            ProgressBar.Value = obj.CompletedPercentage;
            OperationStatus.Text = obj.ProgressStatusText;
            ProgressBarPercentage.Text = string.Format("{0:P}", obj.CompletedPercentage);
        }

internal class WorkClass
    {
        public async Task LongMethod(IReadOnlyList<string> something, IProgress<ProgressInfo> progress)
        {
            await Task.Factory.StartNew(() =>
            {
                var count = something.Count;
                for (int i = 0; i < count; i++)
                {
                    var element = something[i];
                    Thread.Sleep(5);
                    progress.Report(new ProgressInfo((double)(i + 1) / count, element));
                }
            });
        }
    }

S
Sumor, 2015-03-18
@Sumor

Practical guide. Executing an operation in the background
To perform a long-running task, you need to use a BackgroundWorker .
In the DoWork event, describe your long-term work.
As you work (in the DoWork handler), you call the ReportProgress method to inform the main program about the progress of the processing. The ProgressChanged event comes to the main program.
Periodically monitor if anyone is trying to stop your work through CancellationPending.
To start a job, use the RunWorkerAsync method. Through the ProgressChanged event, track progress and update the progress bar. On the RunWorkerCompleted event, you learn about the completion or interruption of work.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question