Мониторинг прогресса в Parallel.ForEach - PullRequest
4 голосов
/ 24 марта 2011

Попытка отслеживать ход цикла Parallel.ForEach Я попробовал предложение, выдвинутое в этом вопросе , но, к сожалению, я все еще не смог выполнить то, что хотел.

В основномПервая проблема, с которой я столкнулся, когда попытался предложить реализацию (используя таймер), заключалась в том, что метод Parallel.ForEach является блокирующим вызовом, и, следовательно, обратный вызов по таймеру не выполнялся.

Поэтому я попытался поместить Parallel.ForEach цикл внутри фонового рабочего потока.Который на самом деле допустил событие таймера, но значение моего счетчика никогда не обновляется до тех пор, пока не завершится операция ForEach.

Вот основная идея кода (с фоновым работником).

private StockList _StockListToProcess = null;

private static Int64 ItemsProcessed = 0;

private System.Windows.Threading.DispatcherTimer _timer = null;

private System.ComponentModel.BackgroundWorker _backWorker = null;

progressBar1.Minimum = 1;
progressBar1.Maximum = this._StockListToProcess.Count;

MainWindow.ItemsProcessed = 0;

this._timer = new System.Windows.Threading.DispatcherTimer();
this._timer.Interval = TimeSpan.FromMilliseconds(100);
this._timer.Tick += timer_Tick;
this._timer.Start();

this._backWorker = new System.ComponentModel.BackgroundWorker();

this._backWorker.DoWork += delegate(object o, System.ComponentModel.DoWorkEventArgs args)
{
    Parallel.ForEach(this._StockListToProcess, new ParallelOptions() { MaxDegreeOfParallelism = 5 },
                     (Stock stock) =>
                         {
                             MyWebServiceClient serviceClient = new MyWebServiceClient ();
                             MyWebServiceClient.ResponseEnum result = (MyWebServiceClient .ResponseEnum)serviceClient.SetProductPricing(token.LoginName, token.LoginPassword, token.SiteID.ToString(), stock.ProductCode, stock.ProductPrice);
                             System.Threading.Interlocked.Increment(ref MainWindow.ItemsProcessed);
                         });

    this._timer.Stop();
};

private void timer_Tick(object sender, EventArgs e)
{
    progressBar1.Value = MainWindow.ItemsProcessed;
}

Чего мне не хватает?

Ответы [ 3 ]

4 голосов
/ 24 марта 2011

Я собираюсь сказать, что вложение таймеров и второстепенных работников вызывает у вас горе.

Если возможно, я советую вам избегать в пользу использования Reactive Extensions для.NET (Rx) .

Вот как бы выглядел ваш код, если бы вы сделали:

progressBar1.Minimum = 1;
progressBar1.Maximum = this._StockListToProcess.Count;

var itemsProcessed = 0;
var updater = new Subject<Unit>(Scheduler.Dispatcher);
updater.Subscribe(u =>
{
    itemsProcessed += 1; //Rx serializes "OnNext" calls so this is safe.
    progressBar1.Value = itemsProcessed;
});

Parallel.ForEach(this._StockListToProcess, new ParallelOptions() { MaxDegreeOfParallelism = 5 },
    (Stock stock) =>
        {
            MyWebServiceClient serviceClient = new MyWebServiceClient ();
            MyWebServiceClient.ResponseEnum result = (MyWebServiceClient .ResponseEnum)serviceClient.SetProductPricing(token.LoginName, token.LoginPassword, token.SiteID.ToString(), stock.ProductCode, stock.ProductPrice);
            updater.OnNext(new Unit());
        });

updater.OnCompleted();

Я провел тест, используя фиктивный бит кода, и он работал нормальнотак что, если вы достаточно смелы, вы сможете запустить это без особых затруднений.: -)

1 голос
/ 28 марта 2011

Хотя я действительно ценю решение, опубликованное Enigmativity после некоторого поиска, я нашел то, что считаю верной реализацией для решения этой проблемы.Тот, который не требует никаких других структур для реализации.

Для полного обзора , пожалуйста, смотрите эту статью.

0 голосов
/ 24 марта 2011

Что если вы используете обычный таймер в своем основном потоке и передаете информацию через ConcurrentDictionary?

...