代码之家  ›  专栏  ›  技术社区  ›  gillyb

如果前一个线程仍然忙,如何让计时器跳过

  •  14
  • gillyb  · 技术社区  · 15 年前

    我在我的服务中创建了一个Timer对象,它每60秒跳一次并调用所需的方法。
    因为我不希望这个计时器在处理找到的新行时滴答作响,所以我将这个方法包装在 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秒,那么下一个滴答作响在上一个完成之前不会进行任何处理。这就是我想要的效果。

    我想发生的是-如果处理需要超过60秒,那么计时器会注意到线程被锁定,然后再等待60秒再次检查,这样我就永远不会陷入线程队列等待前一个线程完成的情况。


    这样做的最佳实践是什么?

    谢谢!

    8 回复  |  直到 15 年前
        1
  •  21
  •   StuartLC    13 年前

    // 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没有指定重入是仅由计时器引起,还是服务是多线程的。假设是后者,但如果是前者,则如果计时器停止(自动重置或手动),则不需要锁定

        2
  •  20
  •   Ani    15 年前

    在这种情况下你不需要锁。在启动前设置timer.AutoReset=false。 之后 每项任务。

        3
  •  8
  •   Andy    15 年前

    在其他答案上的一个类似的变化,它允许计时器保持滴答声,并且只在可以获得锁时才进行工作,而不是停止计时器。

    将其放入已用事件处理程序:

    if (Monitor.TryEnter(locker)
    {
        try
        {
            // Do your work here.
        }
        finally
        {
            Monitor.Exit(locker);
        }
    }
    
        4
  •  7
  •   Scott Chamberlain    15 年前

    快速检查它,看看服务是否正在运行。如果它正在运行,它将跳过此事件并等待下一个事件触发。

    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;
        }
    }
    
        5
  •  4
  •   Community CDub    8 年前

    我建议你在处理时不要让计时器滴答作响。

    将计时器自动重置设置为false。从最后开始。以下是您可能感兴趣的完整答案 Needed: A Windows Service That Executes Jobs from a Job Queue in a DB; Wanted: Example Code

        6
  •  2
  •   Ryan from Denver    15 年前

    其他选项可能是使用BackGroundWorker类或ADPool.QueueUserWorkItem。

    后台工作人员可以轻松地为您检查当前仍在进行的处理并一次处理1个项目的选项。ThreadPool将使您能够继续将项目的每一个记号(如果需要)排队到后台线程。

    根据您的描述,我假设您正在检查数据库队列中的项目。在这种情况下,我将使用线程池将工作推到后台,而不是减慢/停止检查机制。

    对于服务,我建议您考虑使用线程池方法

    例如:

    下面是一个应如何做到这一点的示例:

    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
        }
    
        7
  •  2
  •   James World    12 年前

    有一个很好的方法可以通过反应式扩展来解决这个问题。下面是代码,您可以在这里阅读更完整的解释: http://www.zerobugbuild.com/?p=259

    public static IDisposable ScheduleRecurringAction(
        this IScheduler scheduler,
        TimeSpan interval,
        Action action)
    {
        return scheduler.Schedule(
            interval, scheduleNext =>
        {
            action();
            scheduleNext(interval);
        });
    }
    

    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();
    
        8
  •  0
  •   Chris Ortiz    15 年前

    另一种可能性是这样的:

    void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
    {   
        if (System.Threading.Monitor.IsLocked(yourLockingObject))
           return;
        else
           lock (yourLockingObject)
           // your logic  
               ;
    }
    
    推荐文章