执行异步调用的 Web 服务
我有一个 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什么时候收集服务对象?这是否会阻止后台线程正确执行到底?后台线程还有其他副作用吗?
感谢您的任何意见。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
从我从您的代码中看到的情况来看,我想说的是,当后台工作人员和 AsyncUpdateNumber 类的实例相互引用时,它们都不会被收集,并且没有代码可以破坏此循环引用。
循环引用是由引用BackgroundWorker 的AsyncUpdateNumber 类创建的,并向BackgroundWorker 注册一个事件,创建对AsyncUpdateNumber 类实例的引用。
那么你能做什么......考虑使用以下选项之一来代替BackgroundWorker:
示例 1:
示例 2:
示例 3:
编辑:
回答您的问题来自原始帖子:执行线程的方法始终运行直到完成,无论声明它的方法是否退出。即使上面的匿名方法也会运行直到完成。这里应用的概念称为闭包(AFAIK),在匿名方法的情况下,它甚至使所有引用的变量保持活动状态,即使它们没有在方法本身中声明。
但是,您的类是循环引用的一个主要示例,只有在应用程序过程完成后才会回收该引用。这是 .NET 中更微妙的事情之一,即使在托管系统中,也可能导致内存泄漏......事件创建强引用。
虽然,在单个调用的情况下,这不会造成任何问题,但一旦您多次调用,它可能确实会成为问题。
From what I see from your code I'd say that neither the Background worker, nor the instance of the AsyncUpdateNumber class gets collected as they reference each other and there is no code that breaks this circular reference.
The circular reference is created by the AsyncUpdateNumber class referencing the BackgroundWorker and registering an event with the BackgroundWorker creating a reference to the instance of the AsyncUpdateNumber class.
So what can you do ... consider using one of the following options instead of the BackgroundWorker:
Sample 1:
Sample 2:
Sample 3:
Edit:
To answer your question from the original post: The method executing the thread always runs until it is completed, irrespectively whether the method declaring it exits or not. Even the anonymous methods above will run until completed. The concept applied here is called a closure (AFAIK), in case of the anonymous methods it even keeps all referenced variables alive even if they are not declared within the method itself.
However, your class is a prime example of a circular reference that will only be reclaimed once the application process finishes. It's one of these more subtle things in .NET that - even in a managed system - can cause memory leaks ... events create strong references.
Although, in case of a single call this does not cause any problem, once you hit some calls more it might become a problem indeed.
将单个 Web 方法重写
为两个异步委托:
它们将显示为名为
FetchNumber()
的单个 Web 方法。BeginFetchNumber()
中的BeginInvoke()
将异步执行。我必须在AsyncUpdater
中编写一个名为DoLongRunningThing
的方法,以便委托执行。将您需要从构造函数异步完成的任务移至此新方法中。希望这有帮助。Rewrite the single web method
as two with and an asynchronous delegate:
These will appear as a single web method called
FetchNumber()
. TheBeginInvoke()
inBeginFetchNumber()
will execute asynchronously. I had to make up a method calledDoLongRunningThing
that's inAsyncUpdater
for the delegate to execute. Move the task you're needing done async off of the constructor into this new method. Hope this helps.