具有计时器控制的线程池操作的 C# WinService
我有一个 Windows Service 类(继承自 ServiceBase ),在构造时提供了一个对象列表。 每个操作都描述一个 DoWork() 虚拟方法。
Service 类的重点是管理所有底层操作,在运行时将它们添加/删除到列表中,并在 ThreadPool 线程中执行它们的 DoWork() 方法。
每个Operation都有一个System.Timers.Timer对象,该对象被实例化并与Operation类一起运行。每个操作都会公开一个事件,以通知管理类它自己的计时器已到,并且必须在线程中触发自己的 DoWork() 方法。
TimedService 类将每个经过的计时器事件绑定到从池中请求线程的方法:
private void CheckOperationsList(object source, EventArgs e)
{
try
{
foreach (Operation op in this._operationsList)
{
lock (this._operationsList)
{
op.TimerElapsed += new Operation.ElapsedEventHandler(this.RequestThreadFromPool);
}
}
}
catch (Exception ex) { this._ManEV.WriteError(this._serviceName, ex.Message); }
}
当然,RequestThreadFromPool(object sender, EventArgs e) 方法执行以下操作:
ThreadPool.QueueUserWorkItem(new WaitCallback(((Operation)sender).DoWork), new object());
我从进程接收到奇怪的行为。我尝试实现一个虚拟操作,计时器设置为 10 秒,这只会让处理器占用几秒钟:
for (Int16 i = 0; i < Int16.MaxValue; i++)
{
for (short j = 0; j < short.MaxValue; j++)
{ }
}
每次操作在队列中单独运行时(我将仅包含一个元素的列表传递给服务)是正常的。该进程生成自己的线程,让 CPU 保持运行几秒钟,然后离开。
我实现了上面的虚拟方法的更轻版本(基本相同,没有内部循环)来模拟轻量级线程。
一旦我在队列中获得两个或多个操作(使用不同的计时器!),所有线程都会生成三到四次,然后一切都会停止。
我根本没有任何活动。在 Visual Studio 中,每个操作的计时器似乎已停止计时 - 我没有收到对事件的调用,也没有收到对应该处理事件的委托的调用;每个操作类都有自己的计时器,并且始终通过 this 引用计时器。 !
我尝试监视所有 catch{} ,并尝试将每个 DoWork() 方法包装在 try{} 中,但没有发现任何异常。
令我困惑的是,如果我运行几个相同的操作(即两个或三个长操作或两个或三个短操作),一切都会正常运行。看起来事件来自另一个班级,一切都变得一团糟。
我开始认为 System.Timers.Timer 类是上述所有问题的原因 - 我尝试在调用 Event 之前调用 Stop() 到计时器,但无济于事。 我对每个操作的 DoWork() 方法进行 4-5 次迭代,然后一切都平静下来。
编辑我忘了澄清:我运行的每个操作都是从基本操作继承的。即:
public class TestLongOperation : Operation
{
public TestLongOperation(double secondsInterval) : base(secondsInterval) { }
public override void Work(object buh)
{
for (Int16 i = 0; i < Int16.MaxValue; i++)
{
for (short j = 0; j < short.MaxValue; j++)
{ }
}
}
}
有什么建议吗?如果以上还不够,我可以提供更多代码。 提前致谢。
I have got a Windows Service class ( inherits from ServiceBase ) which at construction time is provided with a List of objects.
Each Operation describes a DoWork() virtual method.
The point of of the Service class is to manage all the underlying Operations, add/remove them to the List at runtime and execute their DoWork() method in a ThreadPool thread.
Each Operation has a System.Timers.Timer object which is instantiated and run with the Operation class. Each Operation exposes up an event to signal the managing class that its own timer is up and it has to fire its own DoWork() method in a thread.
The TimedService class binds each elapsed timer event to a method that requests a thread from the pool:
private void CheckOperationsList(object source, EventArgs e)
{
try
{
foreach (Operation op in this._operationsList)
{
lock (this._operationsList)
{
op.TimerElapsed += new Operation.ElapsedEventHandler(this.RequestThreadFromPool);
}
}
}
catch (Exception ex) { this._ManEV.WriteError(this._serviceName, ex.Message); }
}
And of course, the RequestThreadFromPool(object sender, EventArgs e) method does the following thing:
ThreadPool.QueueUserWorkItem(new WaitCallback(((Operation)sender).DoWork), new object());
I am receiving weird behavior from the processes. I have tried to implement a dummy Operation with a timer set to 10 seconds which simply keeps the processor occupied for a couple of seconds:
for (Int16 i = 0; i < Int16.MaxValue; i++)
{
for (short j = 0; j < short.MaxValue; j++)
{ }
}
Every time an Operation runs alone in the queue ( I pass a List of only one element to the Service ) everything is normal. The process spawns its own thread, keeps the CPU up for a couple of seconds, then leaves.
I implemented a lighter version of the dummy method above ( basically the same without the inner loop ) to simulate a lightweight thread.
As soon as I get two or more operations in the queue ( with different timers! ) all the threads spawn three or four times and then everything stops.
I get no activity at all. In Visual Studio it looks like the Timer of each Operation has stopped ticking - I get no calls to the event, I get no calls to the delegate that is supposed to handle the event; each Operation class has its own timer and always refer to the timer via this. !
I tried monitoring all the catch{}es, and tried wrapping each DoWork() method in a try{} , but I am getting no exceptions.
What is puzzling me is that if I run several identical Operations ( i.e. two or three long operations or two or three short ones ) everything runs as normal. Looks like as the Event comes from another class, everything gets messed up.
I am starting to think that the System.Timers.Timer class is the cause of all the above - I have tried calling Stop() to the timer right before calling the Event, but to no avail.
I get 4-5 iterations of each Operation's DoWork() method and then everything quites down.
edit I forgot to clarify: Each Operation I run is inherited from the base Operation. I.e:
public class TestLongOperation : Operation
{
public TestLongOperation(double secondsInterval) : base(secondsInterval) { }
public override void Work(object buh)
{
for (Int16 i = 0; i < Int16.MaxValue; i++)
{
for (short j = 0; j < short.MaxValue; j++)
{ }
}
}
}
Any suggestions? I can provide more code if the above is not sufficient.
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我有一种感觉,计时器会在每个刻度事件上创建一个新线程,我的服务存在问题,如果在下一个刻度开始时仍在进行上一个刻度的大量处理,则会导致生成大量线程。
我最终完全删除了计时器并用循环替换了它。然后在我的主处理线程中添加了 Thread.sleep。这就像一个计时器,但不会继续生成线程。此外,在处理完成之前循环不会结束。
例如:(
主要主题)
这有帮助吗?
I have a feeling the timer creates a new thread on each tick event, I had a problem with my service that caused lots of threads to be spawned if lots of processing was still going on from the previous tick when the next tick starts.
I ended up removing the timer altogether and replaced it with a loop. Then in my main processing thread I added a Thread.sleep. This then acts like a timer but does not keep spawning threads. Also, the loop does not finish until the processing is finished.
eg:
(Main Thead)
does that help at all?
一些随机的想法:
当程序挂起时,您是否按下了break并检查是否处于几条“锁定”行上。
这些是一次性计时器吗?如果是这样,请在启用计时器之前连接事件处理程序。如果不是,你的工作是可重入的吗?
在计时器处理程序中,为什么要生成另一个线程来完成工作?计时器已经将其放置在新线程中。也许你遇到了线程池耗尽的情况?如果多个计时器处理程序启动并耗尽了线程池,那么它们将挂起等待启动工作线程。
阅读 Timer.SynchronizingObject 属性 以查看该 Timer可以使用线程池。
Some random thoughts:
When the program hangs, did you hit break and cheeck if you were on a couple of 'lock' lines.
Are these one shot timers? If so, did wire up the event handler before enabling the timer. If not, is your work reentrant?
In the timer handler, why are you spawning another thread to do the work? The timer already placed it in a new thread. Maybe you hit thread pool exhaustion? If several timerhandlers start and use up the threadpool, then they would hang waiting to start the work threads.
Read Timer.SynchronizingObject Property to see that Timer can use the ThreadPool.