Web服务执行异步调用

时间:2010-05-31 18:31:57

标签: c# asynchronous garbage-collection asmx backgroundworker

我有一个Web服务方法FetchNumber(),它从数据库中获取一个数字,然后将其返回给调用者。但就在它将数字返回给调用者之前,它需要将此数字发送给另一个服务,以便实例化并运行BackgroundWorker,其工作是将此号码发送给另一个服务。

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;

}

我不想在将此号码发送到另一个服务时阻止Web服务(FetchNumber()),因为它可能需要很长时间而且调用者不关心将号码发送到另一个服务。来电者希望尽快返回。

FetchNumber()使后台工作程序运行它,然后完成(当工作程序在后台线程中运行时)。我不需要任何进度报告或从后台工作者返回值。这更像是一个昙花一现的概念。

我的问题是这个。由于Web服务对象是按照方法调用实例化的,当被调用的方法(在这种情况下为FetchNumber())完成时会发生什么,而它实例化并运行的后台工作程序仍在运行?

后台线程会发生什么? GC何时收集服务对象?这是否会阻止后台线程正确执行到最后?后台线程是否还有其他副作用?

感谢您的任何意见。

2 个答案:

答案 0 :(得分:0)

从我的代码中看到的我会说,后台工作者和AsyncUpdateNumber类的实例都没有收集,因为它们互相引用,并且没有代码打破这个循环引用。

循环引用由引用BackgroundWorker的AsyncUpdateNumber类创建,并使用BackgroundWorker注册事件,创建对AsyncUpdateNumber类实例的引用。

那么你能做什么...考虑使用以下选项之一而不是BackgroundWorker:

  1. 使用线程。
  2. 使用BeginInvoke。
  3. 使用ThreadPool。
  4. 示例1:

    var thread = new Thread(new ParameterizedThreadStart((v) => { /* do stuff */ }));
    thread.Start(value);
    

    示例2:

    var func = new Action<int>(v => { /* do stuff */ });
    func.BeginInvoke(value, null, null);
    

    示例3:

    var threadProc = new Action<object>(v => { /* do stuff - note v is of tyoe object */ });
    ThreadPool.QueueUserWorkItem(new WaitCallback(threadProc));
    

    <强> 编辑:

    从原始帖子回答你的问题:执行线程的方法总是运行直到它完成,无论声明它的方法是否退出。即使是上面的匿名方法也会一直运行到完成。这里应用的概念称为闭包(AFAIK),在匿名方法的情况下,即使它们未在方法本身内声明,它甚至可以保留所有引用的变量。

    但是,您的类是循环引用的主要示例,只有在应用程序进程完成后才会回收它。这是.NET中这些更微妙的东西之一 - 即使在托管系统中 - 也会导致内存泄漏...... 事件会创建强大的引用

    虽然在单次调用的情况下这不会导致任何问题,但是一旦你打了一些电话,它可能会成为一个问题。

答案 1 :(得分:0)

重写单一网络方法

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

作为两个with和一个异步委托:

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);
}

这些将显示为名为FetchNumber()的单个网络方法。 BeginInvoke()中的BeginFetchNumber()将异步执行。我必须在DoLongRunningThing中编写一个名为AsyncUpdater的方法,以便委托执行。将您需要执行的任务从构造函数中异步移动到此新方法中。希望这会有所帮助。