Search code examples
c#multithreadingbackgroundworker

C# BackgroundWorker occasionally taking a long time to start


I am seeing an issue with my use of BackgroundWorker. What happens is that the BackgroundWorker process will work fine, and then occasionally it will take around 40-50 seconds to begin.

The problem has occured on two machines. The BackgroundWorker operation was working fine throughout the day, and then in the afternoon this issue started appearing where the process would take 40 seconds instead of 1 second, but only happened occasionally. You could do 5 of them fine and the 6th would take 40 seconds. You could then do another 5 fine. If it does start taking longer than a second, you can just press cancel on the workingDialog and do it again immediately, and it will work. On further inspection it appears that the worker is not starting the DoWork method immediately.

Could I be running out of threads or something? Or perhaps there is an issue with how I am disposing the BackgroundWorker? The code is below;

using (workingDialog = new Dialogs.WaitDialog()) {
    StartThreadedWork(MyDoWorkMethod, customerID);

    // if user presses cancel button
    if (workingDialog.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) {
        m_Worker.RunWorkerCompleted -= ThreadFinished;
    }
}
m_Worker.Dispose();
m_Worker = null;

The 'StartThreadedWork' method is below;

private void StartThreadedWork(DoWorkEventHandler method, int customerID) {
    m_Worker = new BackgroundWorker();
    m_Worker.DoWork += method;
    m_Worker.RunWorkerCompleted += ThreadFinished;
    object[] parameters = new object [] {customerID };
    m_Worker.RunWorkerAsync(parameters);
}

And finally, the method assigned to the DoWork event handler is;

private void ThreadSyncing(object sender, DoWorkEventArgs e) {
    object[] parameters = e.Argument as object[];
    int customerID = (int)parameters[0];
    Customer resultCustomer = new Customer(FormsManager.Instance.BillingWebService.Customer_GetByCustomerID(customerID, CustomerSyncOption.Default));
    e.Result = resultCustomer;
}

Once the RunWorkerCompleted event is fired, the dialog result is set to OK and the dialog closes.

Any pointers on this would be greatly appriciated!

thanks,


Solution

  • How do you know it was actually the BGW's start that was delayed? You don't give any feedback, all you can see it is that it completed. Looks like you are using a web service, having a server not respond, or complete a request, for 40 seconds isn't abnormal. The default tcp/ip connection timeout is 45 seconds.

    The ThreadPool scheduler could play a role, it tries to limit the number of executing TP threads to the number of cpu cores the machine has. Getting a 40 second delay is however a heavy corner case. The scheduler allows another TP thread to start if existing ones don't complete, twice a second. You would have thus to have 80 active TP threads to incur a delay like that.

    One thing that's notable about the dialog you use, it doesn't actually do anything to stop the worker thread. It just abandons it. So you could get build-up if the server is unresponsive and the user gets impatient. Hitting the Cancel button repeatedly after tapping her foot four times. That does cause steadily increasing delays. You are supposed to use the CancelAsync() method and implement canceling logic using the CancellationPending property. But yes, that's not typically easy to do with a web service.

    Solve that problem by giving better feedback. When the user cancels then don't unsubscribe the RunWorkerCompleted event. Just set a flag that indicates the last request was cancelled. Show in your UI that the web service is unavailable until the worker completes. Including not allowing the user to start the web service request again.