views:

129

answers:

2

I have a webservice method FetchNumber() that fetches a number from a database and then returns it to the caller. But just before it returns the number to the caller, it needs to send this number to another service so instantiates and runs the BackgroundWorker whose job is to send this number to another service.

public class FetchingNumberService : System.Web.Services.WebService
{
    [WebMethod]
    public int FetchNumber() 
    {
        int value = Database.GetNumber();
        AsyncUpdateNumber async = new AsyncUpdateNumber(value);
        return value;
    }
}

public class AsyncUpdateNumber
{
    public AsyncUpdateNumber(int number)
    {
        sendingNumber = number;

        worker = new BackgroundWorker();
        worker.DoWork += asynchronousCall;
        worker.RunWorkerAsync();
    }

    private void asynchronousCall(object sender, DoWorkEventArgs e)
    {
        // Sending a number to a service (which is Synchronous) here
    }

    private int sendingNumber;
    private BackgroundWorker worker;

}

I don't want to block the web service (FetchNumber()) while sending this number to another service, because it can take a long time and the caller does not care about sending the number to another service. Caller expects this to return as soon as possible.

FetchNumber() makes the background worker and runs it, then finishes (while worker is running in the background thread). I don't need any progress report or return value from the background worker. It's more of a fire-and-forget concept.

My question is this. Since the web service object is instantiated per method call, what happens when the called method (FetchNumber() in this case) is finished, while the background worker it instatiated and ran is still running?

What happens to the background thread? When does GC collect the service object? Does this prevent the background thread from executing correctly to the end? Are there any other side-effects on the background thread?

Thanks for any input.

A: 
Obalix
What if BackgroundWorker is only a method-scoped local variable of the AsyncUpdateNumber's constructor , and not its field? The important question for me is not only whether the objects will be collected, but will the thread complete its work even if the starting thread has no references to the background workers' thread?
kornelijepetak
The method will complete as you started the worker in the constructor of the AsyncUpdateNumber class. However, you creating a memory leak as all the calls creating new objects that are never collected. If you *have* to use the BackgroundWorker you should de-register the event instance from the BackgroundWorker as soon as the Worker has started. Once the method is completed set the BackgroundWorker to null. However I think that this is the long way around ... see the samples and their simplicity.
Obalix
I can't do it simply this way because I need the async part of the job separated into a class (for reusability issues). I might be able to put your samples into a class, but that also leaves me with the original question. Since the first thread only creates an instance of the Async class as a local variable, it should be removed when it goes out of scope (i.e. method finishes). What happens to the other thread when it finishes its work (whether BW, Thread, BeginInvoke or Pool). Does the GC have a separate reference tree for each thread?
kornelijepetak
See edit. Be ensured that the thread will run to an end. However, you seriously should reconsider your worker class.
Obalix
A: 

Rewrite the single web method

[WebMethod]
public int FetchNumber() 
{
    int value = Database.GetNumber();
    AsyncUpdateNumber async = new AsyncUpdateNumber(value);
    return value;
}

as two with and an asynchronous delegate:

public delegate AsyncUpdateNumber GetAsyncUpdateNumber(object state, int value);

[WebMethod]
public IAsyncResult BeginFetchNumber(AsyncCallback cb, object state) 
{
    int value = Database.GetNumber();
    AsyncUpdateNumber async = new AsyncUpdateNumber(value);
    GetAsyncUpdateNumber getAsyncUpdateNumber = new GetAsyncUpdateNumber(async.DoLongRunningThing);

    return getAsyncUpdateNumber.BeginInvoke(state, cb, getAsyncUpdateNumber);
}

[WebMethod]
public int EndFetchNumber(IAsyncResult res) 
{
    GetAsyncUpdateNumber getAsyncUpdateNumber = (GetAsyncUpdateNumber)res.AsyncState;

    return getAsyncUpdateNumber.EndInvoke(res);
}

These will appear as a single web method called FetchNumber(). The BeginInvoke() in BeginFetchNumber() will execute asynchronously. I had to make up a method called DoLongRunningThing that's in AsyncUpdater for the delegate to execute. Move the task you're needing done async off of the constructor into this new method. Hope this helps.

Jesse C. Slicer