Search code examples
c#multithreadingwinformsbackgroundworker

How can I avoid global variables in a Threaded application


I made a simple application that calls a slow performing task using background workers. I would like to be able to pass some arguments to the slow function and get results when the task is complete. I am able to do this by using global variables a, b and product. Is there a better way to pass arguments to the slow function and get back the result?

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

using System.Threading;
using System.ComponentModel; // for BackgroundWorker

namespace ThreadsDelegate
{
    /// <summary>
    /// Description of MainForm.
    /// </summary>
    public partial class MainForm : Form
    {
        Random rnd = new Random();
        int a; // will be used by the slow task
        int b; // will be used by the slow task
        string product = null; // we get back the result of the slow task
        int i = 0; // counter

        public MainForm()
        {
            InitializeComponent();
            listBox1.Items.Add("Starting a long task of multiplication, that takes a few seconds.");
            listBox1.Items.Add("We can still use the form interface for other activities, such as generating random numbers...");
            listBox1.Items.Add("We can call the multiplication function multiple times by pressing the button and it will return multiple answers");
            listBox1.Items.Add("When the multiplication task is complete, the results will be displayed");
        }

        void BtnRandomClick(object sender, EventArgs e)
        {
            int number = rnd.Next(0, 1000); 
            tbRandomNumber.Text = number.ToString();
        }

        void BtnSlowTaskClick(object sender, EventArgs e)
        {
            a = rnd.Next(1, 9);  // we use global variables to pass data to slow function
            b = rnd.Next(11, 22); // we use global variables to pass data to slow function

            BackgroundWorker bg = new BackgroundWorker();
            bg.DoWork += new DoWorkEventHandler(SlowMultiply);
            bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
            bg.RunWorkerAsync();
        }

        void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            listBox1.Items.Add((++i) + ". Result of multiplication: " + product);
        }

        void SlowMultiply(object sender, DoWorkEventArgs e)
        {
            int prod = a * b;

            for (int i = 1; i <= 5; i++)
            {
                Thread.Sleep(500);
            }

            product = prod.ToString(); // update the global variable
        }       
    }
}

Solution

  • You can send parameters through a background worker like this:

    int a =  rnd.Next(1, 9);
    int b =  rnd.Next(11, 22);
    var data = new Tuple<int, int>(a, b);
    bg.RunWorkerAsync(argument: data);  
    

    And then in your worker method

    void SlowMultiply(object sender, DoWorkEventArgs e)
            {
                var mydata = (Tuple<int, int>) e.Argument;
                int product = mydata.Item1 * mydata.Item2;
    
    
                for (int i = 1; i <= 5; i++)
                {
                    Thread.Sleep(500);
                }
    
                product = prod.ToString(); // update the global variable
            }       
        }
    

    And alternatively, you could just calculate the random numbers inside of your worker method.