C# 如果前一个线程仍然繁忙,如何让计时器跳过
我创建了一个windows服务,它应该每隔60秒检查数据库中的某个表中的新行。对于添加的每一行,我都需要在服务器上执行一些繁重的处理,有时可能需要60秒以上 我在我的服务中创建了一个计时器对象,它每60秒滴答一次并调用所需的方法。C# 如果前一个线程仍然繁忙,如何让计时器跳过,c#,multithreading,windows-services,timer,C#,Multithreading,Windows Services,Timer,我创建了一个windows服务,它应该每隔60秒检查数据库中的某个表中的新行。对于添加的每一行,我都需要在服务器上执行一些繁重的处理,有时可能需要60秒以上 我在我的服务中创建了一个计时器对象,它每60秒滴答一次并调用所需的方法。 因为我不希望这个计时器在处理找到的新行时滴答作响,所以我将这个方法包装在一个lock{}块中,这样其他线程就无法访问它 它看起来像这样: Timer serviceTimer = new Timer(); serviceTimer.Interval = 60; ser
因为我不希望这个计时器在处理找到的新行时滴答作响,所以我将这个方法包装在一个
lock{}
块中,这样其他线程就无法访问它
它看起来像这样:
Timer serviceTimer = new Timer();
serviceTimer.Interval = 60;
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
serviceTimer.Start();
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
lock (this)
{
// do some heavy processing...
}
}
现在,我想知道-如果我的计时器滴答作响,并且在数据库中发现许多新行,而现在处理将花费60秒以上,那么下一个滴答作响将不会进行任何处理,直到上一个滴答作响完成。这就是我想要的效果 但是现在,serviceTimer_Appeased方法是在第一次处理完成后立即关闭,还是等待计时器再次计时 我想做的是——如果处理需要超过60秒,计时器会注意到线程被锁定,然后再等待60秒再次检查,这样我就不会陷入线程队列等待前一个线程完成的情况 我如何才能达到这个结果?
这样做的最佳实践是什么
谢谢 您可以尝试在处理过程中禁用计时器,例如
// Just in case someone wants to inherit your class and lock it as well ...
private static object _padlock = new object();
try
{
serviceTimer.Stop();
lock (_padlock)
{
// do some heavy processing...
}
}
finally
{
serviceTimer.Start();
}
编辑:OP没有指定重入是仅由计时器引起的,还是服务是多线程的。假设是后者,但如果是前者,则如果计时器停止(自动重置或手动),则无需锁定。快速检查服务是否正在运行。如果它正在运行,它将跳过此事件并等待下一个事件触发
Timer serviceTimer = new Timer();
serviceTimer.Interval = 60;
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
serviceTimer.Start();
bool isRunning = false;
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
lock (this)
{
if(isRunning)
return;
isRunning = true;
}
try
{
// do some heavy processing...
}
finally
{
isRunning = false;
}
}
其他选项可能是使用BackGroundWorker类或ADPool.QueueUserWorkItem 后台工作人员可以很容易地为您提供选项检查当前仍在进行的处理,并一次处理1项。线程池将使您能够在每次勾选(如有必要)时继续将项目排队到后台线程 根据您的描述,我假设您正在检查数据库队列中的项目。在这种情况下,我将使用线程池将工作推到后台,而不是减慢/停止检查机制 对于服务,我建议您考虑使用线程池方法。这样,您可以使用计时器每60秒检查一次新项目,然后将其排队,让.Net计算出分配给每个项目的金额,然后继续将项目推送到队列中 例如:如果您只使用计时器,并且有5个新行,总共需要65秒的处理时间。使用线程池方法,这将在65秒内完成,有5个后台工作项。使用计时器方法,这将需要4分钟以上的时间(即您在每行之间等待的时间),此外,这可能会导致其他正在排队的工作的备份日志 下面是一个应如何进行此操作的示例:
Timer serviceTimer = new Timer();
void startTimer()
{
serviceTimer.Interval = 60;
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
serviceTimer.AutoReset = false;
serviceTimer.Start();
}
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
// Get your rows of queued work requests
// Now Push Each Row to Background Thread Processing
foreach (Row aRow in RowsOfRequests)
{
ThreadPool.QueueUserWorkItem(
new WaitCallback(longWorkingCode),
aRow);
}
}
finally
{
// Wait Another 60 Seconds and check again
serviceTimer.Stop();
}
}
void longWorkingCode(object workObject)
{
Row workRow = workObject as Row;
if (workRow == null)
return;
// Do your Long work here on workRow
}
在这种情况下你不需要锁。在启动计时器之前,设置timer.AutoReset=false。
处理完成后,重新启动处理程序中的计时器。这将确保计时器在每个任务完成60秒后启动。我建议您在处理计时器时,不要让计时器滴答作响 将计时器自动重置设置为false。从最后开始。这是你可能感兴趣的完整答案
另一种可能性是这样的:
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (System.Threading.Monitor.IsLocked(yourLockingObject))
return;
else
lock (yourLockingObject)
// your logic
;
}
TimeSpan interval = TimeSpan.FromSeconds(5);
Action work = () => Console.WriteLine("Doing some work...");
var schedule = Scheduler.Default.ScheduleRecurringAction(interval, work);
Console.WriteLine("Press return to stop.");
Console.ReadLine();
schedule.Dispose();
其他答案也有类似的变化,允许计时器一直滴答作响,只有在可以获得锁时才工作,而不是停止计时器 将其放入已用事件处理程序:
if (Monitor.TryEnter(locker)
{
try
{
// Do your work here.
}
finally
{
Monitor.Exit(locker);
}
}
有一种很好的方法可以通过反应式扩展解决这个问题。这是代码,您可以在这里阅读更完整的解释: 你可以这样使用它:
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (System.Threading.Monitor.IsLocked(yourLockingObject))
return;
else
lock (yourLockingObject)
// your logic
;
}
TimeSpan interval = TimeSpan.FromSeconds(5);
Action work = () => Console.WriteLine("Doing some work...");
var schedule = Scheduler.Default.ScheduleRecurringAction(interval, work);
Console.WriteLine("Press return to stop.");
Console.ReadLine();
schedule.Dispose();
与我的解决方案相比,我更喜欢nonnb的解决方案,但我将保留我的解决方案,以便在您无法阻止事件触发时提供一个示例。此外,允许事件继续触发在某些情况下也很有用。您可以记录它或在主进程之外执行一些其他处理。不要使用
lock(this)
-将autoreset设置为false会更容易,那么我们就不需要锁定任何东西这也是我使用的方法。已用事件处理程序刚好失效,计时器可以不使用。这是一个非常酷的解决方案,并展示了RX调度程序的一个有趣的用法,这可能不是很明显。这个解决方案对我来说效果很好。也让我对调度程序有了新的认识