Hi,
I have a special problem that I wonder how it could be possible to solve.
I have 2 backgroundworkers that I know are, asynchronous events. I start the 2 backgroundworkers with the startButton. The workers have a loop with Thread.Sleep(1).

With the setFlagButton, I set the variable like this: block = "notbusy";

In this moment, I want only one of the backgroundworkers to report progress and show the messageBox. Either MessageBox.Show("From a1") or MessageBox.Show("From a2").

Sometimes only one of the messageboxes are shown but sometimes both are shown. How can this be solved so only one messagebox will be shown each time?

        private void setFlagButton_Click(object sender, EventArgs e)
        {
            block = "notbusy";
        }



        BackgroundWorker a1 = new BackgroundWorker();
        BackgroundWorker a2 = new BackgroundWorker();
        String block = "";
        private void startButton_Click(object sender, EventArgs e)
        {
            block = "busy";

            //Activate Workers
            if (a1.IsBusy == false) 
            {
                a1 = new BackgroundWorker(); 
                a1.WorkerReportsProgress = true; 
                a1.DoWork += new DoWorkEventHandler(a1_DoWork); 
                a1.ProgressChanged += new ProgressChangedEventHandler(a1_ProgressChanged); 
                a1.RunWorkerAsync(); 
            }
            if (a2.IsBusy == false) 
            { 
                a2 = new BackgroundWorker(); 
                a2.WorkerReportsProgress = true; 
                a2.DoWork += new DoWorkEventHandler(a2_DoWork); 
                a2.ProgressChanged += new ProgressChangedEventHandler(a2_ProgressChanged); 
                a2.RunWorkerAsync(); 
            }
        }
        private void a1_DoWork(object sender, DoWorkEventArgs e) 
        { 
            while (true) 
            {
                while (block == "busy") 
                {
                    Thread.Sleep(1); 
                }
                block = "busy"; 
                a1.ReportProgress(1); 
            }
        }
        private void a2_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                while (block == "busy")
                {
                    Thread.Sleep(1);
                }
                block = "busy";
                a2.ReportProgress(1);
            }
        }
        private void a1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
        {
            MessageBox.Show("From a1"); 
        }
        private void a2_ProgressChanged(object sender, ProgressChangedEventArgs e) 
        { 
            MessageBox.Show("From a2"); 
        }

Recommended Answers

All 5 Replies

The global variable block can be tested and modified by both workers. This may be unsafe.
I suggest to use Monitor class to lock the variable before using it :
For instance :
Monitor.Enter(block);
block = "busy";
Monitor.Exit(block);

Thank you,

I think I need to ask the question as the scenario exactly is. This is the exact scenario of the problem.

I run 2 instances of an application. In each instance, I have a backgroundworker that runs exactly the same code.
Now, in this code there is a critical area when those 2 backgroundworker cant be at the very same time in moment. I will use the method "SendMessage" which can be used to send a String between the 2 instances. With that technique, I would be able to set the variable "AnyWorkerIsInCriticalCode = true" and "AnyWorkerIsInCriticalCode = false".

So the practical scenario would exactly look like the below. So my question is how we can assure that only 1 backgroundworker executes at a time in the critical code area?

        bool AnyWorkerIsInCriticalCode = false;
        private void a1_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                while (AnyWorkerIsInCriticalCode == true) { Thread.Sleep(1); }

                AnyWorkerIsInCriticalCode = true; /*Use SendMessage to update this variable in other instance*/
                /*Only 1 worker can be in this critical code at a time*/
                //
                //Critical code
                //
                AnyWorkerIsInCriticalCode = false; /*Use SendMessage to update this variable in other instance*/
            }
        }
        private void a2_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                while (AnyWorkerIsInCriticalCode == true) { Thread.Sleep(1); }

                AnyWorkerIsInCriticalCode = true; /*Use SendMessage to update this variable in other instance*/
                /*Only 1 worker can be in this critical code at a time*/
                //
                //Critical code
                //
                AnyWorkerIsInCriticalCode = false; /*Use SendMessage to update this variable in other instance*/
            }
        }

caKus is on the right track, although not a complete enough answer.

First, let me try and explain what can go wrong with your code. Let's assume AnyWorkerIsInCriticalCode is false. So a1_DoWork() skips the while-loop. If a2_DoWork() checks the while-loop condition before a1_DoWork() sets the variable to true, it will skip the while-loop as well. There's another issue with this as well, the compiler may optimize the code to store the boolean variable in a register and skip checking the system RAM each time (note that the volatile keyword could help with this issue, but does not fix the first issue).

So, like caKus said, you can use the Monitor class. What he should have mentioned is that it should be wrapped in a try-finally block, like so:

public class MyClass
{
    private readonly object _lock = new object();

    private void a1_DoWork(object sender, DoWorkEventArgs e)
    {
        Monitor.Enter(_lock);
        try
        {
            //Critical code here.
        }
        finally
        {
            Monitor.Exit(_lock);
        }
    }

    private void a2_DoWork(object sender, DoWorkEventArgs e)
    {
        Monitor.Enter(_lock);
        try
        {
            //Critical code here.
        }
        finally
        {
            Monitor.Exit(_lock);
        }
    }
}

This is a verbose option, which can be simplified by the lock statement like so:

public class MyClass
{
    private readonly object _lock = new object();

    private void a1_DoWork(object sender, DoWorkEventArgs e)
    {
        lock(_lock)
        {
            //Critical code here.
        }
    }

    private void a2_DoWork(object sender, DoWorkEventArgs e)
    {
        lock(_lock)
        {
            //Critical code here.
        }
    }
}

Keep in mind that the variable you lock on should be private (or atleast as low access as possible) and not a string or type (something that can be shared with necessarily refering to the same thing).

This also offers an advantage. The thread doesn't need to keep polling the boolean variable, it just blocks and waits for the other thread to finish with the "lock", which saves some CPU cycles.

Let me know if you need more clarification.

When a thread encounter the instruction Monitor.Enter(block)
- if the memory block is not locked, the thread lock the variable and continue the execution.
- if the memory block is locked by another thread, the present thread will stop until memory is unlocked.
The Monitor.Exit(block) will unlock the memory.
So, if you enclose your sensible code between a Monitor.Enter and a Monitor.Exit, I think it will do what you asked.

Thank you nMaillet and caKus,

This is very good news. I was able to confirm that this works with the below code, where we only want the file to write "1" on each row. Which confirms that only one critical code is running.

With the code below, if we now assume that each backgroundworker is running in a different instance of an application and we are using "SendMessage" to pass a string between the instances and using this technique to block a variable.
I wonder if that would be safe and work?

The procedure would be something like this:
SendMessage to instance ---> ReceiveMessage in instance ---> lock this instance variable

instead of just:
lock this instance variable

        private readonly String block = ""; int TestNum = 0;
        private void a1_DoWork(object sender, DoWorkEventArgs e)
        {
            String filePath = "D:/test.txt";
            while (true)
            {
                Monitor.Enter(block);
                try
                {
                    TestNum++;
                    FileStream qr2 = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                    StreamWriter writeFile = new StreamWriter(qr2);
                    writeFile.WriteLine(TestNum.ToString()); writeFile.Close(); qr2.Close();
                    TestNum = 0;
                }
                finally
                {
                    Monitor.Exit(block);
                }
                Thread.Sleep(1);
            }
        }
        private void a2_DoWork(object sender, DoWorkEventArgs e)
        {
            String filePath = "D:/test.txt";
            while (true)
            {
                Monitor.Enter(block);
                try
                {
                    TestNum++;
                    FileStream qr2 = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                    StreamWriter writeFile = new StreamWriter(qr2);
                    writeFile.WriteLine(TestNum.ToString()); writeFile.Close(); qr2.Close();
                    TestNum = 0;
                }
                finally
                {
                    Monitor.Exit(block);
                }
                Thread.Sleep(1);
            }
        }
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.