Reputation: 1261
I am just trying to update a progress bar in Parallel.ForEach
and the application seems to freeze.
Despite I have already tried to use Invoke
solution as suggested on other posts, I still have problems.
This is a simplified version of my code:
public partial class Form1 : Form
{
event EventHandler ReportProgress;
class ProgressEvent : EventArgs
{
public int Total { get; set; }
public int Current { get; set; }
}
public Form1()
{
InitializeComponent();
ReportProgress += Form1_ReportProgress;
}
private void Form1_ReportProgress( object sender, EventArgs e )
{
var progress = e as ProgressEvent;
if ( InvokeRequired )
{
this.Invoke( new Action( () => { progressBar.Maximum = progress.Total; progressBar.Value = progress.Current; } ) );
}
}
private void buttonStart_Click( object sender, EventArgs e )
{
// Create a list with seed to be used later
List<int> values = new List<int>() { };
values.AddRange( Enumerable.Range( 1, 15 ) );
var total = values.Count();
var current = 0;
Parallel.ForEach( values, v =>
{
Interlocked.Increment( ref current );
var r = new Random( v );
// Just sleep a little
var sleep = r.Next( 10, 1000 );
Thread.Sleep( sleep );
// Update the progress bar
ReportProgress( null, new ProgressEvent() { Current = current, Total = total } );
}
);
MessageBox.Show( "Done " );
}
}
The application seems to hang and the Message box is not shown, while if I remove the event generation (ReportProgress
) everything works perfectly, but obviously the progress bar is not update at all.
Upvotes: 0
Views: 3069
Reputation: 22001
You could change:
this.Invoke( new Action( () => { progressBar.Maximum = progress.Total; progressBar.Value = progress.Current; } ) );
to:
this.BeginInvoke( new Action( () => { progressBar.Maximum = progress.Total; progressBar.Value = progress.Current; } ) );
This will execute the action on an async thread.
Upvotes: 1
Reputation: 2009
I edit your code. If you want to screen do not freeze, probably easy way is using background worker for performing operation.
When you use background worker, UI thread is not blocked.Hence, user still can interact with UI.
public partial class Form1 : Form
{
event EventHandler ReportProgress;
class ProgressEvent : EventArgs
{
public int Total { get; set; }
public int Current { get; set; }
}
public Form1()
{
InitializeComponent();
ReportProgress += Form1_ReportProgress;
}
private void Form1_ReportProgress(object sender, EventArgs e)
{
var progress = e as ProgressEvent;
this.Invoke(new Action(() =>
{
progressBar.Maximum = progress.Total;
progressBar.Value = progress.Current;
}));
}
private void button1_Click(object sender, EventArgs e)
{
// Collect data from UI, I use list for example
List<int> values = new List<int>() { };
values.AddRange(Enumerable.Range(1, 100));
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += BackgroundWorkerOnDoWork;
backgroundWorker.RunWorkerAsync(values);
}
private void BackgroundWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
var values = (IEnumerable<int>) doWorkEventArgs.Argument; // the 'argument' parameter resurfaces here
var total = values.Count();
var current = 0;
Parallel.ForEach(values, v =>
{
Interlocked.Increment(ref current);
var r = new Random(v);
// Just sleep a little
var sleep = r.Next(10, 1000);
Thread.Sleep(sleep);
// Update the progress bar
ReportProgress(null, new ProgressEvent() {Current = current, Total = total});
}
);
MessageBox.Show("Done ");
}
}
Upvotes: 1