0

I am currently implementing a task that takes a lot of time. Basically, what I want to do is to run multiple tasks inside foreach loop. I tried using Parallel.ForEach its freezing my UI. I wanna be able to call like 10 uids at a time.

foreach (var uid in listBox2.Items)
              {
                  if (StopEmail) break;
                  Application.DoEvents();
                  string jsonstring = GetEmails(uid.ToString(), token);
                  if (jsonstring != null)
                  {
                      label6.Text = " Current UID: " + listBox2.Items.IndexOf(uid);
                      dynamic jsonResponse = JsonConvert.DeserializeObject(jsonstring);
                      string idstt = jsonResponse["email"];
                      if (idstt != null)
                      {
                          listBox3.Items.Add(idstt);
                          label4.Text = "Total Emails: " + listBox3.Items.Count.ToString();

                      }
                  }
              }

Here is my Parallel.ForEach Code:

var files = listBox2.Items.Cast<String>().ToList();
Parallel.ForEach(files, uid =>
{
    Application.DoEvents();
    string jsonstring = GetEmails(uid.ToString(), token);
    if (jsonstring != null)
    {
        this.Invoke(new MethodInvoker(delegate()
        {
            label6.Text = " Current UID: " + listBox2.Items.IndexOf(uid);
        }));

        dynamic jsonResponse = JsonConvert.DeserializeObject(jsonstring);
        string idstt = jsonResponse["email"];
        if (idstt != null)
        {
            this.Invoke(new MethodInvoker(delegate()
            {
                listBox3.Items.Add(idstt);
                label4.Text = "Total Emails: " + listBox3.Items.Count.ToString();
            }));
        }
    }
});
8
  • If this is freezing your UI, then you are running the outer part of the foreach on the UI thread. Try running it on a background worker so your UI thread is free to keep drawing. and then from the background thread try using parallel.ForEach Commented Dec 4, 2016 at 23:09
  • is this WPF or Winforms Commented Dec 4, 2016 at 23:14
  • @meganaut can you please check my edit of parallel.foreach code. Commented Dec 4, 2016 at 23:14
  • @M.kazem Akhgary WinForms Commented Dec 4, 2016 at 23:15
  • @SherryMemon The problem is the method that parallel.foreach is running in is on the main or UI thread. Do a little research on backgroundWorkers to find out how to move that function OFF the main ui thread. Commented Dec 4, 2016 at 23:18

2 Answers 2

2

Here's the approach I would take - use Microsoft's Reactive Framework (NuGet "System.Reactive.Windows.Forms").

Then you can do this:

var uids = listBox2.Item.Cast<String>().ToArray();

var query =
    from uid in uids.ToObservable()
    from jsonstring in Observable.Start(() => GetEmails(uid, token))
    where jsonstring != null
    select new { uid, jsonstring };

IDisposable subscription =
    query
        .ObserveOn(this)
        .Subscribe(x =>
        {
            label6.Text = " Current UID: " + listBox2.Items.IndexOf(x.uid);
            dynamic jsonResponse = JsonConvert.DeserializeObject(x.jsonstring);
            string idstt = jsonResponse["email"];
            if (idstt != null)
            {
                listBox3.Items.Add(idstt);
                label4.Text = "Total Emails: " + listBox3.Items.Count.ToString();
            }
        });

The slow part is in the GetEmails call so that is all nicely handled in the query.

The .ObserveOn(this) call marshals the code back to the UI thread for you so no messy .Invoke calls needed.

And finally, to end the computation early just call subscription.Dispose().

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

1 Comment

Yuhoo! Seems, awesome.
0

Wrap your Parallel.ForEach inside a Task.Run call:

var files = listBox2.Items.Cast<String>().ToList();
Task task = Task.Run( () => {

    Parallel.ForEach( files, uid =>
    {
        // remove the `Application.DoEvents()` call, it is unnecessary
        // ...
    }

} );

It may be a better design to instead return data from the ForEach method instead of calling .Invoke as well, that way you can use await and update the UI a single go when the ForEach is complete - but that depends on the nature of your loop and your desired UX.

4 Comments

How can I make it run like 10 uids at a time. I mean 10 tasks,
@SherryMemon Why 10 specifically? The Parallel.ForEach method will see how many hardware threads your computer has and run the correct number to ensure the list is processed optimally. So you must have a very good reason to override ForEach's thread count.
I wanna keep checking how many uids are checked and all that. that''s why I am updating UI.
it's doing a bad behavior like sometime it's jumping on uid 2776 and then jumps on uid 29.

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.