Пробовал через класс Progress, но тут мне не понравилось плодить большое кол во классов через
Progress.Report(new MyReport{...});
Ничего страшного в этом нет - эти объекты на самом деле тут же умирают, не покидая первого поколения GC.
Тут другой вопрос более важен: класс Progress устроен так, что маршаллит все репорты в SynchronizationContext, в котором он был создан. В случае с WinForms - это контекст UI-потока. Если ваш Task слишком часто будет вызывать метод Report, это может негативно сказаться на UI-потоке, т.к. он будет забит репортами, каждый из которых он должен обработать. Более того, поскольку маршаллинг в классе Progress асинхронный, есть вероятность, что задача будет выполнена до того, как на UI-потоке обработаются все репорты. Получется запаздывание прогресс-бара от реального прогресса (и вот тут как раз возникает вероятность перехода репортов в Gen1 и Gen2).
Я бы написал свою реализацию класса Progress, которая использует таймер, периодически опрашивающий последнее обновление прогресса. Так сделано в большинстве систем, где обновление может происходить часто, например, при копировании множества файлов. Примерно так:
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();
}
}
}
Использовать его можно примерно так:
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);
где:
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));
}
});
}
}