Search code examples
c#taskbackgroundworker

ReportProgress doesn't call progressChanged with tasks in c#


 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {            
        int currentProgress=-1;

        while (currentProgress<length)
        {
            currentProgress=Worker.progress;
            backgroundWorker1.ReportProgress(currentProgress);
            Thread.Sleep(500);
            length = Worker.UrlList.Count;
        }
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        int ix = e.ProgressPercentage;
        progressBar1.Value = ix;
        lblText.Text =ix+" %";
    }

I wrote a program to download page sources by reading a file have about 1000 URLs. so I used Tasks to download pages async. here Worker.progress is the currently executed URL amount. though the debuger hits the backgroundWorker1.ReportProgress(currentProgress); it never enter to the backgroundWorker1_ProgressChanged.

 private void StartButton_Click(object sender, EventArgs e)
    {            
        t.makeUrlList(inputFile);

        backgroundWorker1 = new BackgroundWorker();
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.DoWork += backgroundWorker1_DoWork;
        backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
        backgroundWorker1.RunWorkerAsync();
        t.RunTasks();
        Application.Exit();
    }

background worker initializes when start button clicks...

here is where my tasks are created....

public void RunTasks()
    {
        if (numOfTasks > UrlList.Count)
            numOfTasks=UrlList.Count-1;
       Task[] t = new Task[numOfTasks];
        int j = 0;
        while ( j < UrlList.Count-1)
        {

           for (int i = 0; (i < t.Count())&&(j<UrlList.Count-1); i++)
            {


                try
                {
                    if (t[i].IsCompleted || t[i].IsCanceled || t[i].IsFaulted)
                    {
                        t[i] = Task.Run(() => FindWIN(j));
                        j++;
                        progress = j;                            
                    }
                }
                catch (NullReferenceException ex)
                {
                    t[i] = Task.Run(() => FindWIN(j));
                    j++;
                    progress = j;
                }
                }
            }
        }

Solution

  • See the demo code below. This is mostly untested, and certainly isn't 'production standard', but it should give you a good start!

    1. It uses a ConcurrentQueue to hold the list of URLs to be processed. This is threadsafe, and makes life a lot easier.

    2. It has a configurable number of urls and tasks. It's best not to make 1000 tasks, but instead have a queue of work items, and a smaller pool of Tasks which 'pull items' off the queue until it's empty. This means you can performance test different numbers of Tasks and find the best value for your problem.

    3. It uses Invoke when updating the progress bar - this avoids the cross-thread exception.

    4. No BackgroundWorker - just TaskFactory and Task

      public partial class Form1 : Form
      {
          private const int UrlCount = 1000;
          private const int taskCount = 10;
          private ConcurrentQueue<string> urlList;
          private List<Task> taskList;
      
          public Form1()
          {
              InitializeComponent();
          }
      
          private void ResetQueue()
          {
              // fake up a number of strings to process
              urlList = new ConcurrentQueue<string>(Enumerable.Range(0, UrlCount)
                        .Select(i => "http://www." + Guid.NewGuid().ToString() + ".com"));
          }
      
          private void button1_Click(object sender, EventArgs e)
          {
              ResetQueue();
              var taskFactory = new TaskFactory();
              // start a bunch of tasks
              taskList = Enumerable.Range(0, taskCount).Select(i => taskFactory.StartNew(() => ProcessUrl()))
                                    .ToList();
          }
      
          void ProcessUrl()
          {
              string current;
      
              // keep grabbing items till the queue is empty
              while (urlList.TryDequeue(out current))
              {
                  // run your code
                  FindWIN(current);
      
                  // invoke here to avoid cross thread issues
                  Invoke((Action)(() => UpdateProgress()));
              }
          }
      
          void FindWIN(string url)
          {
              // your code here
              // as a demo, sleep a sort-of-random time between 0 and 100 ms
              Thread.Sleep(Math.Abs(url.GetHashCode()) % 100);
          }
      
          void UpdateProgress()
          {
              // work out what percentage of the queue is processed
              progressBar1.Value = (int)(100 - ((double)urlList.Count * 100.0 / UrlCount));
          }
      }