1

I have a list of Contacts as follows.

List<string> Numbers = new List<string>();

This list can have any length of numbers in it. I want to send message to these numbers one by one and also report the progress to the user on a WinForm so I have a method like this.

private void ProcessBroadcast()
        {
            foreach (var number in Numbers)
                {
                    //Send a Message here
                    messageWorker.RunWorkerAsync(message);
                }
        } 

Since sending a message takes time so we opted to use BackgroundWorker to send a message. Now here the issue is we want to continue to send message to the next number in the foreach loop only when Background worker has completed.

I have implemented

messageWorker_DoWork

&

messageWorker_RunWorkerCompleted

and I am able to get the Number in messageWorker_RunWorkerCompleted method. I have done my research but couldn't find anything related to this.

Sajjad Arif Gul
  • 89
  • 2
  • 11
  • If you're running the worker once for each number, why not just count how many times you've called it? – stuartd Jun 20 '18 at 10:47
  • It would make much more sense to place `foreach (var number in Numbers)` _inside_ the DoWork method. Your current code will fail because you can't restart a Bgw, and you only want 1 running. – bommelding Jun 20 '18 at 10:49
  • Take a look at ConcurrentQueue – Ferdinand Swaters Jun 20 '18 at 10:51
  • @bommelding I cant place it in background worker because I need to show progress on datagridview on form for each number when the message is sent. – Sajjad Arif Gul Jun 20 '18 at 11:01
  • 2
    That is why the Bgw has an OnProgress event. – bommelding Jun 20 '18 at 11:03
  • Personally... id use an `actionblock`, since `RunWorkerAsync` looks IO bound i.d use `async` and `await` make a list with a reference to the row and number feed/post that to the `actionblock`, and run it all in parallel update the grid when each one finished.. wallah – TheGeneral Jun 20 '18 at 11:04
  • @TheGeneral - no, RunWorkerAsync is the granddaddy of async/await. But still going strong. – bommelding Jun 20 '18 at 11:05
  • @bommelding ahh yup, i had to slip back into 2008 for a second, actually cancel all the above.. – TheGeneral Jun 20 '18 at 11:07

2 Answers2

1
    private void ProcessBroadcast()
    {
        //foreach (var number in Numbers)
        //{
        //    //Send a Message here
        //    messageWorker.RunWorkerAsync(message);
        //}
        messageWorker.EnableProgressReporting = true;
        messageWorker.RunWorkerAsync(Numbers);
    } 

The DoWork

// this runs on a background pool thread
void DoWork(object sender, args)
{
    var worker = sender as Backgroundworker;
    var Numbers = args.Argument as IEnumerable<SomeThing>;
    int percentage = 0;    

    foreach (var number in Numbers)
    {
        //Send a Message here
        worker.ReportProgress(percentage, number);

        // other processing on number, just don't use the UI
    }
}

Your Progress and Completed eventhandlers will be executed on the main thread.

(none of this was checked by a compiler)

bommelding
  • 2,969
  • 9
  • 14
-2

Use a WaitHandle like code below :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;


namespace ConsoleApplication51
{
    class Program
    {
        static AutoResetEvent block = new AutoResetEvent(false);
        static void Main(string[] args)
        {
            block.Reset();
            Test test = new Test();
            block.WaitOne();
        }
    }
    public class State
    {
        public string messsage = "";
    }
    public class Test
    {
        BackgroundWorker messageWorker = new BackgroundWorker();

        static AutoResetEvent autoEvent = new AutoResetEvent(false);

        List<int> Numbers = new List<int>() { 1,2,3,4,5};

        public Test()
        {
            messageWorker.DoWork += new DoWorkEventHandler(messageWorker_DoWork);
            messageWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(messageWorker_RunWorkerCompleted);
            ProcessBroadcast();
        }
        public void ProcessBroadcast()
        {
            foreach (int number in Numbers)
            {
                //Send a Message here
                autoEvent.Reset();
                messageWorker.RunWorkerAsync(number);
                autoEvent.WaitOne();
            }
        }

        private void messageWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int? number = e.Argument as int?;
            BackgroundWorker worker = sender as BackgroundWorker;

            State state = new State() { messsage = "Done"};


        }
        private void messageWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            autoEvent.Set();
        }






    }




}
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • 1
    No, this is a bad (actually: silly) answer. Mainly because a Console program is a poor substitute for a WinForms app, especially when it comes to background work. – bommelding Jun 20 '18 at 11:23
  • I just put it into a console application for testing. The only real difference between a form and a console application is the form has a block so it doesn't terminate. Will add to code. – jdweng Jun 20 '18 at 11:30
  • No, the most fundamental difference is the message loop (running in the ApplicationContext). – bommelding Jun 20 '18 at 11:39
  • A Backgroundworker in a Console app is a solution w/o the problem. – bommelding Jun 20 '18 at 11:40
  • The code backgroundworker will be the same in either a console application or a form application. Your comment does not make any sense. – jdweng Jun 20 '18 at 12:07
  • The bgw was _designed_ for WinForms and WPF. It has absolutely no business in any kind of process that does not run a messageloop. – bommelding Jun 20 '18 at 12:14
  • As a hint, try to use the Progress event in your Console app. – bommelding Jun 20 '18 at 12:17
  • I was going to use the progress event, but it was more complicated than the solution I posted. The WaitHandle will do exactly the same without having to generate another event. The Progress Event sill would need a WaitHandle. – jdweng Jun 20 '18 at 14:21
  • Explain to the rubber duck what the bgw is good for in your app. – bommelding Jun 20 '18 at 14:24
  • I made the code as simple as possible to demonstrate the correct way of handling a loop for a backgroundworker. It is only sample code and I tested and it works. – jdweng Jun 20 '18 at 14:30
  • "it works" - no doubt. But it misses the point completely. Harmfully. – bommelding Jun 20 '18 at 14:32
  • The OP asked for following : "Now here the issue is we want to continue to send message to the next number in the foreach loop only when Background worker has completed." – jdweng Jun 20 '18 at 14:35
  • Which was an X/Y question. – bommelding Jun 20 '18 at 14:49