Monitoring Progress in Parallel.ForEach

10,248

Solution 1

Although I do appreciate the solution posted by Enigmativity after some searching I have found what I consider the correct implementation for solving this problem. One that does not require any other frameworks to implement.

For a full overview please see this article.

Solution 2

I'm going to take a stab at saying the nesting of timers and background workers is causing you grief.

If possible, I suggest you avoid then in favour of using the Reactive Extensions for .NET (Rx).

This is what your code would look like if you did:

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();

I did a test using a dummy bit of code and it worked fine so, if you are brave enough, you should be able to get this running without to much difficulty. :-)

Share:
10,248
Maxim Gershkovich
Author by

Maxim Gershkovich

Developer with experience in. ASP.NET Azure Point of sale software C# VB.NET .NET Framework Sharepoint MVC Microsoft Kinect for Windows 1.8 &amp; 2

Updated on June 04, 2022

Comments

  • Maxim Gershkovich
    Maxim Gershkovich almost 2 years

    Attempting to monitor progress of a Parallel.ForEach loop I have tried the suggestion put forward in this question but unfortunately I have still been unable to accomplish what I wanted.

    Basically the first problem I ran into when I attempted the implementation suggested (using a timer) was that the Parallel.ForEach method is a blocking call and hence the timer-tick callback was not occurring.

    So I tried putting the Parallel.ForEach loop inside of a background worker thread. Which did infact allow for the timer-tick event to occur but my counter value is never updated until the ForEach operation is complete.

    Here is the basic idea of the code (with the backgroundworker).

    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;
    }
    

    What am I missing?