Search code examples
c#async-awaitprogressiprogressreportprogress

IProgress<T> how often to report progress?


When using IProgress<T> to report progress, should it be

  • the responsibility of the code reporting progress to limit its progress reports to a frequency that is "reasonable", -or-
  • the responsibility of the specific implementation of IProgress<T> to be aware that the reporting of progress might be at a higher frequency than is reasonable for the way it will be presenting this progress.

The context of the question is I have some code which uses IProgress<T> to report progress, and it reports progress at a very high rate. I want to display progress with a UI progress bar. If I use the provided Progress<T> implementation (which posts progress to the UI SyncronizationContext), then it causes the UI to be unresponsive (i.e. there are so many messages sent to the message queue, that the user can't even click the "Cancel" button on the dialog).

So,

  • I could fix this by reporting less, but what if I had an IProgress<T> implementation that just wrote progress to a log file (and could handle the high reporting frequency). -or-
  • I could fix this by creating my own specific IProgress<T> implementation that limited how often I processed/reported progress. Presumably, this implementation would record the latest progress on a non-UI thread, and then (perhaps) the UI would updated based on a timer.

Solution

  • Write a decorator which throttles the calls. This way you separate the logic of throttling and the actual reporting, and you can use it for any other IProgress<T> implementation.

    Use this decorator when you want to throttle the progress reporting. Simple wrap your progress reportor with instance of the class below.

    I've left the throttling logic up to you. You can make it time-based, amount-of-calls-based or some other criteria.

    public class ProgressThrottler<T>: IProgress<T> {
        public ProgressThrottler(IProgress<T> progress) {
            _progress = progress ?? throw new ArgumentNullException("progress");
        }
    
        private readonly IProgress<T> _progress;
    
        public void Report(T value) {
            // Throttles the amount of calls
            bool reportProgressAfterThrottling = ...;
    
            if (reportProgressAfterThrottling) {
                _progress.Report(value);
            }
        }
    }