2010-05-31 168 views
1

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

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何时收集服务对象?这是否会阻止后台线程正确执行到最后?后台线程有没有其他副作用?

感谢任何输入。

回答

0

从我从你的代码中看到我想说的是,他们相互引用,没有代码,打破循环引用既没有后台工作,也不是AsyncUpdateNumber类的实例被收集。

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

所以,你能做些什么?考虑使用下列选项而不是BackgroundWorker的一个:

  1. 使用线程。
  2. 使用BeginInvoke。
  3. 使用ThreadPool。

样品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中这些更微妙的事情之一 - 即使在管理系统中 - 可能导致内存泄漏... 事件创建强引用

尽管在单次调用时这不会引起任何问题,但是一旦您再次发出一些呼叫,它确实可能会成为问题。

+0

如果BackgroundWorker只是AsyncUpdateNumber的构造函数的方法范围的局部变量,而不是其字段?对我来说,重要的问题不仅是这些对象是否会被收集,而且即使起始线程没有引用后台工作线程,线程是否会完成其工作? – 2010-05-31 20:39:50

+0

当您在AsyncUpdateNumber类的构造函数中启动worker时,该方法将完成。但是,由于所有调用都会创建永远不会收集的新对象,因此会创建内存泄漏。如果*有*使用BackgroundWorker,只要Worker已经启动,您应该立即从BackgroundWorker注销事件实例。方法完成后,将BackgroundWorker设置为null。不过,我认为这是漫长的...看样品和简单。 – AxelEckenberger 2010-05-31 20:54:14

+0

我不能这样做,因为我需要把作业的异步部分分成一个类(用于可重用性问题)。我可能会把你的样本放入一个班级,但这也让我面对原始问题。由于第一个线程只创建一个Async类的实例作为局部变量,所以当它离开作用域时(即方法结束),它应该被删除。当其他线程完成其工作时(无论是BW,Thread,BeginInvoke还是Pool),会发生什么情况。 GC是否为每个线程都有单独的参考树? – 2010-06-01 06:37:37

0

重写单卷筒纸方法

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

为两个与和异步委托:

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()单个web方法。 BeginFetchNumber()中的BeginInvoke()将异步执行。我不得不为AsyncUpdater编写一个名为DoLongRunningThing的方法供委托执行。将您需要从构造函数中完成异步的任务移至此新方法中。希望这可以帮助。