4
\$\begingroup\$

I create multiple BackgroundWorker within a for-loop and each of them needs to know a special value. To simplify, it is just i in this example. When the BackgroundWorker is finished, I need to read that i again. I thought of subclassing BackgroundWorker and creating a class MyBW for that purpose which is able to store the i as value.

My example below works, but I am interested, if this is the best way to do this?

Edit: I have to add, that the variable I need to pass is a simple String, not a large object.

Fully working minimal example

using System;
using System.Text;

namespace MultiThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                MyBW bw = new MyBW();
                bw.value = i;
                bw.WorkerReportsProgress = false;
                bw.DoWork += delegate(object sender, System.ComponentModel.DoWorkEventArgs eargs)
                {
                    Console.WriteLine(String.Format("Thread {0} started", bw.value));
                    System.Threading.Thread.Sleep(bw.value * 1000);
                };
                bw.RunWorkerCompleted += delegate(object sender, System.ComponentModel.RunWorkerCompletedEventArgs eargs)
                {
                    Console.WriteLine(String.Format("Thread {0} finished", bw.value));
                };

                bw.RunWorkerAsync();
            }

            // Only as a run loop that I can see the output.
            while (true)
            {
                System.Threading.Thread.Sleep(1000);
                Console.WriteLine("Sleeping...");
            }
        }
    }

    class MyBW : System.ComponentModel.BackgroundWorker
    {
        /// <summary>
        /// Here I store the value.
        /// </summary>
        public int value;
    }
}
\$\endgroup\$
  • \$\begingroup\$ Oh, this doesn't look good. Unfortunatelly I cannot rewrite it withasync/await but let's wait for someone who can ;-D \$\endgroup\$ – t3chb0t Jul 27 '16 at 18:12
  • 1
    \$\begingroup\$ Do you have to use BackgroundWorker? Unless you're targeting an old .NET version, you really should be using newer constructs. \$\endgroup\$ – 404 Jul 27 '16 at 19:40
  • \$\begingroup\$ Hello! Please don't make changes to the original post once it has been reviewed, as that invalidates the current answers. Please see our meta side on performing iterative reviews for more information! \$\endgroup\$ – syb0rg Jul 27 '16 at 20:01
  • \$\begingroup\$ Have a look at Task, BackgroundWorker are really oldschool. blog.stephencleary.com/2012/02/… \$\endgroup\$ – dhcgn Jul 28 '16 at 13:52
6
\$\begingroup\$

BackgroundWorker already has a mechanism for passing and retrieving arguments. The DoWorkEventArgs has a property for a passed in method argument and a property for the DoWork result. The RunWorkerCompletedEventArgs has a property for retrieving the result. No sub-classing is necessary, although you may need to make a custom container if you need to pass/return more than one object.

for (int i = 0; i < 10; i++)
{
  BackgroundWorker bw = new BackgroundWorker ();
  bw.WorkerReportsProgress = false;
  bw.DoWork += delegate(object sender, System.ComponentModel.DoWorkEventArgs eargs)
  {
    int i = (int)eargs.Arugment;  //get argument
    Console.WriteLine(String.Format("Thread {0} started", bw.value));
    System.Threading.Thread.Sleep(bw.value * 1000);
    eargs.Result = i;            //set result
  };
  bw.RunWorkerCompleted += delegate(object sender, System.ComponentModel.RunWorkerCompletedEventArgs eargs)
  {
    int i = (int)eargs.Arugment;  //get argument
    Console.WriteLine(String.Format("Thread {0} finished", i));
  };

  bw.RunWorkerAsync(i);
}
\$\endgroup\$
  • 1
    \$\begingroup\$ there is a bug; you defined an i inside the loop that already defines it \$\endgroup\$ – t3chb0t Jul 27 '16 at 18:49
  • \$\begingroup\$ but aways it's a questionable design because a BackgroundWorker needs to be disposed and it's hard manage it with the OP's loop \$\endgroup\$ – t3chb0t Jul 27 '16 at 18:51
  • \$\begingroup\$ If you are referring to the i inside the delegates, I was thinking that it would mask the outer for loop i since it is in the inner scope of a delegate. If not though, it is trivial to rename. As for the bgworkers in a loop, I've tried it before and yes, the results were mixed. \$\endgroup\$ – Zack Jul 27 '16 at 19:26
0
\$\begingroup\$

The solution I came up with, thanks to the many hints in the comments and answer is as following:

using System;
using System.ComponentModel;
using System.Text;
using System.Threading.Tasks;

namespace MultiThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Run the async method 10 times.
            for (int i = 0; i < 10; i++)
            {
                RunSomethingAsync(i);
            }

            // Only as a run loop so that the output is visible.
            while (true)
            {
                System.Threading.Thread.Sleep(500);
                Console.WriteLine("Sleeping...");
            }
        }

        static async void RunSomethingAsync(int i)
        {
            int z = 99;
            Task t = Task.Run(() =>
            {
                System.Threading.Thread.Sleep(i * 5000);
                z = 77;
            });
            // Output is always 99.
            Console.WriteLine(String.Format("started thread {0}: {1}", i, z));
            await t;
            // Output is always 77.
            Console.WriteLine(String.Format("finished thread {0}: {1}", i, z));
        }
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.