background worker is busy although not working

73 Views Asked by At

I'm using BackGrondWorker in my Desktop application form to load some data from the Internet, but I faced a strange problem. after load the form I click on radio button on the form called "RdMain" that button to fire the BGW:

private void RdMain_CheckedChanged(object sender, EventArgs e)
{
    if (BgwCodeLevel.IsBusy)
    {
        BgwCodeLevel.CancelAsync();
    }
    xAccounts.xClsMain.ShowPB();
    BgwCodeLevel.RunWorkerAsync();
}

so it's working well but when I click On another radio button called "RdSub" that has the same code exactly as "RdMain" I get an error BGW is currently busy although it's not!!! Error

when you see the code you will see that everything is ok as I think at least.

private void BgwCodeLevel_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        //CheckConn
        if (xAccounts.xClsDb.CheckConn() == false) { Settings.Default.ErrType = 1; return; }

        //Get Code + Level
        xAccounts.AccParent = Convert.ToInt64(CboAccParent.SelectedValue);
        if (RdMain.Checked)
        {
            xAccounts.AccType = "main";
        }
        else
        {
            xAccounts.AccType = "sub";
        }
        xAccounts.Get_AccCode();
        xAccounts.Get_AccLevel();

        Settings.Default.ErrType = 0;
    }
    catch
    {
        Settings.Default.ErrType = 2;
    }
    finally
    {
        xAccounts.xClsDb.CloseConn();
    }
}

//End
private void BgwCodeLevel_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    switch (Settings.Default.ErrType)
    {
        case 0:
            TxtAccCode.Text = xAccounts.AccCode.ToString();
            TxtAccLevel.Text = xAccounts.AccLevel.ToString();
            xAccounts.xClsMain.HidePB();
            break;
        case 1:
            xAccounts.xClsMain.Msg_ErrCon();
            break;
        case 2:
            xAccounts.xClsMain.Msg_ErrOp();
            break;
        case 3:
            xAccounts.xClsMain.Msg_ErrDbl();
            break;
    }
}

note: it's not the first time that I have used BGW and it works very well in all parts of my projects but here I don't know what's the problem!

3

There are 3 best solutions below

0
Hamada On BEST ANSWER

I found the problem. the problem is that when I change the "Rdmain" status the event of "RdMain" will be fired but there is another event that will be fired also, it's "RdSub" and both events will run the same BGW at the same time.

0
Jeroen van Langen On

Why do you think you can cancel the background work? (you never use backgroundWorker.CancellationPending or e.Cancel = true;.)

You have to wait on the RunWorkerCompleted before restarting it. In the Completed you can check if it was canceled. (if you implement a cancel mechanism).

AFAIK the CancelAsync() is not blocking and waiting until it is completed, it will just flip the CancellationPending boolean. (otherwise it will block the UI thread)

This makes it a little harder to manager the work on the BGW when dealing with multiple jobs.


I would fix this by creating a queue. Create a method which checks if there is a current job running. If not, start the background worker with that job, otherwise add it to a queue. When the BGW is completed, it should check the queue for waiting jobs and execute them.

Or you could create a new BGW for each job.

2
JonasH On

Cancellation is cooperative, you can ask some background work to stop working, but that background work still need to periodically check if it should stop working. Just stopping a thread from executing could be dangerous, so it is not done. From the documentation

CancelAsync submits a request to terminate the pending background operation and sets the CancellationPending property to true.

When you call CancelAsync, your worker method has an opportunity to stop its execution and exit. The worker code should periodically check the CancellationPending property to see if it has been set to true.

But when looking at your worker method it is not apparent what is actual taking time. "Slow" methods are typically either compute bound, i.e. contains loops, or IO bound. The former give you a convenient place to check for cancellations, while IO operations should by now mostly use asynchronous operations that accept cancellation tokens.

In any case, you would need to wait for the current background operation to actually finish, successfully or not, before starting the next operation. Background worker makes this a bit cumbersome. My recommendation would be to rewrite using async/await, tasks, cancellationToken, etc, rather than background worker. This tend to be more similar to regular synchronous programming. And it makes it much easier to "await" something.

Also, it is not apparent what xAccounts is, but I would be concerned that it is used from multiple threads without any form of synchronization, are you sure it is thread safe?