20

More newbie questions:

This code grabs a number of proxies from the list in the main window (I couldn't figure out how to make variables be available between different functions) and does a check on each one (simple httpwebrequest) and then adds them to a list called finishedProxies.

For some reason when I press the start button, the whole program hangs up. I was under the impression that Parallel creates separate threads for each action leaving the UI thread alone so that it's responsive?

private void start_Click(object sender, RoutedEventArgs e)
        {
            // Populate a list of proxies
            List<string> proxies = new List<string>();
            List<string> finishedProxies = new List<string>();

            foreach (string proxy in proxiesList.Items)
            {
                proxies.Add(proxy);
            }

            Parallel.ForEach<string>(proxies, (i) =>
            {
                string checkResult;
                checkResult = checkProxy(i);

                finishedProxies.Add(checkResult);
                // update ui
                /*
                 status.Dispatcher.Invoke(
                  System.Windows.Threading.DispatcherPriority.Normal,
                  new Action(
                    delegate()
                    {
                        status.Content = "hello" + checkResult;
                    }
                )); */
                // update ui finished

                
                //Console.WriteLine("[{0}] F({1}) = {2}", Thread.CurrentThread.Name, i, CalculateFibonacciNumber(i));
            });

            
        }

I've tried using the code that's commented out to make changes to the UI inside the Parallel.Foreach and it makes the program freeze after the start button is pressed. It's worked for me before but I used Thread class.

How can I update the UI from inside the Parallel.Foreach and how do I make Parallel.Foreach work so that it doesn't make the UI freeze up while it's working?

Here's the whole code.

Md. Zakir Hossain
  • 1,082
  • 11
  • 24
dsp_099
  • 5,801
  • 17
  • 72
  • 128
  • 3
    You are pummeling the UI thread with invoke requests, it doesn't get around to doing its regular duties anymore. Like repainting the UI. At least lower the priority to Background. – Hans Passant Dec 03 '11 at 06:28
  • @dsp_099б what do you mean under " It's worked for me before but I used Thread class"? – Fulproof Mar 13 '13 at 16:42
  • Anyone else coming here: I suspect this example shouldn't even use Parallel.ForEach. Unless there is a clear benefit to using that, just make a background thread, then process the items *sequentially* within that. Then any Invokes back to UI thread will also be done sequentially, avoiding overloading UI thread with simultaneous requests. (Or use more modern solutions, such as async/await, that also perform actions one at a time.) – ToolmakerSteve Mar 13 '21 at 01:13

6 Answers6

19

You must not start the parallel processing in your UI thread. See the example under the "Avoid Executing Parallel Loops on the UI Thread" header in this page.

Update: Or, you can simply create a new thread manuall and start the processing inside that as I see you have done. There's nothing wrong with that too.

Also, as Jim Mischel points out, you are accessing the lists from multiple threads at the same time, so there are race conditions there. Either substitute ConcurrentBag for List, or wrap the lists inside a lock statement each time you access them.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • 4
    What do you mean under "substitute `ConcurrentBag` for `List`"? ... Did you really mean "substitute `List` to `ConcurrentBag`"? – Fulproof Mar 13 '13 at 16:50
  • 2
    @Fulproof: I believe that correct English is "substitute A *for* B" == "substitute B *with* A". – Jon Mar 13 '13 at 17:01
  • 1
    ["substitute one thing (A) for another (B)"](http://dictionary.reverso.net/english-cobuild/to%20substitute%20a%20for%20b) == "substitute A with B" == (A) "takes place or performs the function of the other" (B). Can you give any reference to your usage? – Fulproof Mar 14 '13 at 02:05
  • Anyway, I'd like to understand better [how to choose between .NET concurrent collections for multi-threading in WPF](http://stackoverflow.com/questions/15400133/when-to-use-blockingcollection-and-when-concurrentbag-instead-of-listt) – Fulproof Mar 14 '13 at 02:38
  • @Fulproof: [Reference](http://en.wiktionary.org/wiki/substitute), examples #2 and #3 are exactly what I 'm referring to. Your own ref does not mention "subtitute with" at all as far as I can see. – Jon Mar 14 '13 at 09:09
  • 2
    Jon, you're right, but as this is an international forum it would have been nicer if a more straightforward expression were used. "Either use ConcurrentBag instead of List or..." would have been a lot easier to read for everyone, I think. Lesson learnt, thought. :) – Bruno Medeiros Aug 23 '17 at 08:14
7

A good way to circumvent the problems of not being able to write to the UI thread when using Parallel statements is to use the Task Factory and delegates, see the following code, I used this to iterate over a series of files in a directory, and process them in a Parallel.ForEach loop, after each file is processed the UI thread is signaled and updated:

var files = GetFiles(directoryToScan);

tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;

Task task = Task.Factory.StartNew(delegate
{
    // Were we already canceled?
    ct.ThrowIfCancellationRequested();

    Parallel.ForEach(files, currentFile =>
    {
        // Poll on this property if you have to do 
        // other cleanup before throwing. 
        if (ct.IsCancellationRequested)
        {
            // Clean up here, then...
            ct.ThrowIfCancellationRequested();
        }

        ProcessFile(directoryToScan, currentFile, directoryToOutput);

        // Update calling thread's UI
        BeginInvoke((Action)(() =>
        {
            WriteProgress(currentFile);
        }));
    });
}, tokenSource.Token); // Pass same token to StartNew.

task.ContinueWith((t) =>
        BeginInvoke((Action)(() =>
        {
            SignalCompletion(sw);
        }))
);

And the methods that do the actual UI changes:

void WriteProgress(string fileName)
{
    progressBar.Visible = true;
    lblResizeProgressAmount.Visible = true;
    lblResizeProgress.Visible = true;

    progressBar.Value += 1;
    Interlocked.Increment(ref counter);
    lblResizeProgressAmount.Text = counter.ToString();
        
    ListViewItem lvi = new ListViewItem(fileName);
    listView1.Items.Add(lvi);
    listView1.FullRowSelect = true;
}

private void SignalCompletion(Stopwatch sw)
{
    sw.Stop();
        
    if (tokenSource.IsCancellationRequested)
    {
        InitializeFields();
        lblFinished.Visible = true;
        lblFinished.Text = String.Format("Processing was cancelled after {0}", sw.Elapsed.ToString());
    }
    else
    {
        lblFinished.Visible = true;
        if (counter > 0)
        {
            lblFinished.Text = String.Format("Resized {0} images in {1}", counter, sw.Elapsed.ToString());
        }
        else
        {
            lblFinished.Text = "Nothing to resize";
        }
    }
}

Hope this helps!

Md. Zakir Hossain
  • 1,082
  • 11
  • 24
StevenVL
  • 166
  • 3
  • 12
  • You might get a further performance boost using `BeginInvoke` onto the UI thread, then you don't have to wait while it's updating, as you currently do when using `Invoke`. Of course, this might require locks inside `WriteProgress`... – Simon MᶜKenzie May 15 '13 at 07:21
  • The UI thread currently isn't being locked, it stays fully responsive, so i'm not sure how this might help? But I'm gonna give it a try to see the difference, locking the objects themselves isn't a problem in my scenario. – StevenVL May 15 '13 at 07:44
  • Ignore my comment about the lock - I was thinking about multiple threads running the `BeginInvoke`, but since they're all being invoked on the UI thread, there can't be any re-entrancy. What I was saying is that each thread in your `Parallel.ForEach` has to wait for the `Invoke` to complete, which will slow things down. With `BeginInvoke`, the UI updates will be queued onto the UI thread and will run asynchronously. – Simon MᶜKenzie May 15 '13 at 07:52
  • Setting up a test case right now to compare, I'll come back here when I got the results, thanks in advance! – StevenVL May 15 '13 at 07:55
  • 1
    Just finished my tests, using the same amount of files to be resized for each call. `Invoke` gave me results from 9.6seconds to 8.2 fastest (only once), whereas `BeginInvoke` was faster, going from 8.1 fastest several times to 8.5 slowest. Thanks for the tip @SimonMcKenzie! – StevenVL May 15 '13 at 08:12
  • No worries. Of course, in a tighter loop or with more involved UI updates, the improvements can be a lot more substantial. – Simon MᶜKenzie May 15 '13 at 08:22
  • VERY Helpful, did not compile first shot but of big help, i implemented it just now, THANKS! – Davide Piras Dec 10 '18 at 17:55
2

If anyone's curious, I kinda figured it out but I'm not sure if that's good programming or any way to deal with the issue.

I created a new thread like so:

Thread t = new Thread(do_checks);
t.Start();

and put away all of the parallel stuff inside of do_checks().

Seems to be doing okay.

dsp_099
  • 5,801
  • 17
  • 72
  • 128
1

if you want to use parallel foreach in GUI control like button click etc then put parallel foreach in Task.Factory.StartNew like

private void start_Click(object sender, EventArgs e)
        {
                await Task.Factory.StartNew(() =>
                     Parallel.ForEach(YourArrayList, (ArraySingleValue) =>
                     {

                Console.WriteLine("your background process code goes here for:"+ArraySingleValue);
                     })
                    );
    }//func end

it will resolve freeze/stuck or hang issue

Hassan Saeed
  • 6,326
  • 1
  • 39
  • 37
1

One problem with your code is that you're calling FinishedProxies.Add from multiple threads concurrently. That's going to cause a problem because List<T> isn't thread-safe. You'll need to protect it with a lock or some other synchronization primitive, or use a concurrent collection.

Whether that causes the UI lockup, I don't know. Without more information, it's hard to say. If the proxies list is very long and checkProxy doesn't take long to execute, then your tasks will all queue up behind that Invoke call. That's going to cause a whole bunch of pending UI updates. That will lock up the UI because the UI thread is busy servicing those queued requests.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Also, I load about 10 proxies each time to run the test so it's not too many; I added a line AFTER the parallel.foreach that changes a label box to 'checker is complete!' and when i press the check button the program WAITS until all of the processes are done before updating the box so it's like yes they all do run simultaneously but it's as if they run simultaneously in the same thread if that makes any sense, because simply doing the httpwebrequest from the same ui thread would cause it to hang up exactly the same way. – dsp_099 Dec 03 '11 at 04:14
1

This is what I think might be happening in your code-base.

Normal Scenario: You click on button. Do not use Parallel.Foreach loop. Use Dispatcher class and push the code to run on separate thread in background. Once the background thread is done processing, it will invoke the main UI thread for updating the UI. In this scenario, the background thread(invoked via Dispatcher) knows about the main UI thread, which it needs to callback. Or simply said the main UI thread has its own identity.

Using Parallel.Foreach loop: Once you invoke Paralle.Foreach loop, the framework uses the threadpool thread. ThreadPool threads are chosen randomly and the executing code should never make any assumption on the identity of the chosen thread. In the original code its very much possible that dispatcher thread invoked via Parallel.Foreach loop is not able to figure out the thread which it is associated with. When you use explicit thread, then it works fine because the explicit thread has its own identity which can be relied upon by the executing code.

Ideally if your main concern is all about keeping UI responsive, then you should first use the Dispatcher class to push the code in background thread and then in there use what ever logic you want to speedup the overall execution.

Timothy G.
  • 6,335
  • 7
  • 30
  • 46
Pawan Mishra
  • 7,212
  • 5
  • 29
  • 39