4

I started off trying to add a progress bar to the windows form that updates the progress of code running within a Parallel.Foreach loop. In order to do this the UI thread has to be available to update the progress bar. I used a Task to run the Parallel.Foreach loop to allow the UI thread to update the progress bar.

The work done within the Parallel.Foreach loop is rather intensive. After running the executables of the program(not debugging within visual studio) with the Task, the program became unresponsive. This is not the case if I run my program without Task. The key difference I noticed between the two instances is that the program takes ~80% of the cpu when ran without Task, and ~5% when ran with Task.

private void btnGenerate_Click(object sender, EventArgs e)
    {
        var list = GenerateList();
        int value = 0;
        var progressLock = new object ();

        progressBar1.Maximum = list.Count();

        Task t = new Task(() => Parallel.ForEach (list, item =>
        {
                DoWork ();
                lock (progressLock)
                {
                    value += 1;
                }
        }));

        t.Start();

        while (!t.IsCompleted)
        {
            progressBar1.Value = value;
            Thread.Sleep (100);
        }
    }

Side Note: I know that

 Interlocked.Increment(ref int___);

works in place of the lock. Is it considered more efficient?

My Question is three fold:

1.) Why would the program with the Task become unresponsive when the load is much less?

2.) Does using Task to run Parallel.Foreach limit the thread pool of the Parallel.Foreach to only the thread running the task?

3.) Is there a way to make the UI thread responsive instead of sleeping for the .1 second duration without using cancellation token?

I'm grateful for any help or ideas, I've spent quite a lot of time researching this. I also apologize if I've violated any posting format or rules. I tried to adhere to them, but may have missed something.

7
  • Waiting for the task to complete in a loop on the UI thread is just like executing it on the UI thread (slightly worse, even). Look into BackgroundWorker or async Commented May 29, 2015 at 1:03
  • If DoWork() access any of your UI elements, it will cause a deadlock to occur. Commented May 29, 2015 at 1:22
  • JohnathonSullinger - DoWork() does not access any UI elements. @Yorye Nathan - Could you elaborate or give an example? I have never done anything with multi-threading before today. Commented May 29, 2015 at 1:37
  • Maybe something related to this? stackoverflow.com/questions/17248680/… Commented May 29, 2015 at 1:46
  • possible duplicate of Support of progress reporting and incremental results in .NET 4.0 "Task Parallel Library" Commented May 29, 2015 at 1:56

1 Answer 1

6

You can greatly simplify your code there by using the built in Invoke method that invokes a delegate on the owning Windows synchronization context.

From MSDN:

Executes the specified delegate on the thread that owns the control's underlying window handle.

The Invoke method searches up the control's parent chain until it finds a control or form that has a window handle if the current control's underlying window handle does not exist yet.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    string[] GenerateList() => new string[500];
    void DoWork()
    {
        Thread.Sleep(50);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var list = GenerateList();
        progressBar1.Maximum = list.Length;

        Task.Run(() => Parallel.ForEach(list, item =>
        {
            DoWork();

            // Update the progress bar on the Synchronization Context that owns this Form.
            this.Invoke(new Action(() => this.progressBar1.Value++));
        }));
    }
}

This will invoke the Action delegate on the same UI thread that the Form belongs to, from within the Task.

Now to try and answer your questions

1.) Why would the program with the Task become unresponsive when the load is much less?

I'm not 100% sure, but this could be related to you locking a member on the UI thread. If the load is less, then the lock will happen more frequently, potentially causing the UI thread to "hang" while the progressbar is incremented.

You are also running a while loop that is sleeping the UI thread every 100 milliseconds. You'll see UI hanging due to that while loop.

2.) Does using Task to run Parallel.Foreach limit the thread pool of the Parallel.Foreach to only the thread running the task?

It does not. Several tasks will get created within the Parallel.ForEach call. The underlying ForEach uses a partitioner to spread the work out, and not create more tasks than what is necessary. It creates tasks in batches, and processes the batches.

3.) Is there a way to make the UI thread responsive instead of sleeping for the .1 second duration without using cancellation token?

I was able to handle that by removing the while loop and using the Invoke method to just go ahead and execute a lambda on the UI thread directly.

Sign up to request clarification or add additional context in comments.

10 Comments

That's a great solution for .Net 4.5, but the question is tagged .net-4.0. Could OP use a BeginInvoke() to update the progressbar?
Ah, I missed that. Yes they can. I'll update my answer with an example.
Thanks for the response! Unfortunately the project I'm working on is using .NET 4.0 which, as far as I know, doesn't support the Progress(T) Class. edit: Didn't see that someone already caught that.
@ENSpeer Microsoft provides Progres<T> in 4.0, you just need to install the NuGet package Microsoft.Bcl
Also sorry to do it but I have to do a -1 for the advice "It's always good to use DoEvents() in your while loops on the UI thread.", its almost never good to do DoEvents() in a loop, if you find you need to use DoEvents() in a loop the correct advice is "you should rewrite your code to not need to do a while loop on the UI thread". You cover the correct way in your answer (use Invoke) but you should not have recommended DoEvents()
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.