سؤال

Recently decided to write a "quick" windows form app to tag my MP3 files. Not done anything with parallelism since .Net 3.0, so I'm looking at the Parallel.ForEach method to deal with the UI locking I get when I'm using a standard foreach statement. Here's an excerpt:

var i = 1;
var files = new List<string>(); // File list is populated using recursive method.

foreach(var f in files) {
    // Add a row
    var row = dgvList.Rows[dgvList.Rows.Add()];

    // Update label
    lblSummary.Text = string.Concat("Processing... ", i);
    // Do things with row

    // Increment progress bar
    progressBar.PerformStep();
    i++;
}

I've figured out the simple usage of Parallel.ForEach(), but I'm not sure I should be using that particular method to update the UI? Any suggestions?

هل كانت مفيدة؟

المحلول 3

OK, I found the best way to achieve this is by running something like this:

// Kick off thread
Task.Factory.StartNew(delegate{
     foreach(var x in files) {
         // Do stuff

         // Update calling thread's UI
         Invoke((Action)(() => {
              progressBar.PerformStep();
         }));
     }
}

I actually updated my code to populate a List within the foreach loop, then assign that to the daragrid via .DataSource, instead of working with the .Rows collection directly. Should have done that from the start really :)

نصائح أخرى

You shouldn't use Parallel Libraries from your UI thread. The parallel library runs a group of tasks on multiple threads so you shouldn't write any UI related code inside it.

What you should do is move your business logic to background tasks and update the UI using dispatcher that will execute it on UI thread

as MSDN says

It is important to keep your application's user interface (UI) responsive. If an 
operation contains enough work to warrant parallelization, then it likely should not
be run that operation on the UI thread. Instead, it should offload that operation to 
be run on a background thread. For example, if you want to use a parallel loop to 
compute some data that should then be rendered into a UI control, you should consider
executing the loop within a task instance rather than directly in a UI event handler. 
Only when the core computation has completed should you then marshal the UI update back 
to the UI thread.

and most importantly if you try to update UI thread from Paralle.Foreach

If you do run parallel loops on the UI thread, be careful to avoid updating UI 
controls from within the loop. Attempting to update UI controls from within a parallel 
loop that is executing on the UI thread can lead to state corruption, exceptions, 
delayed updates, and even deadlocks, depending on how the UI update is invoked

You should be very careful with thread-safety. You should be make sure to lock any object you are using, and unlock it appropriately.

Otherwise, there should be no problem I know of using Parallel.ForEach for UI.

EDIT: you can set Form.CheckForIllegalCrossThreadCalls=false to disable check for thread-safety.
Here's some documentation: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.checkforillegalcrossthreadcalls.aspx
This will work, but it is dangerous, because then you need to care about your thread-safety by yourself.

A better way to deal with this is to use the invoke-pattern for the UI-logic, but then parallelism will suffer, as the UI operation itself will be called on the UI thread.
It is, however, the safe way to do things.
Documentation: http://msdn.microsoft.com/en-us/library/ms171728.aspx

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top