如何用 System.Cron.Schedule 处理长 运行 的作业?

How to handle long running jobs with System.Cron.Schedule?

我正在努力学习 Haskell。作为第一次尝试,我希望编写一个守护进程,定期 运行 为我做备份工作。

我正在使用 System.Cron.Schedule to do the scheduling for me and running the restic 可执行文件进行备份。

因为这是一个备份作业,它可能需要一个日志时间到 运行,以防止下一个计划的备份从 运行ning 开始,而前一个备份还没有完成,我想确保我只能拥有作业 运行ning 的 1 个实例。如何防止长时间 运行ning 作业再次 运行?

作为一个简化的示例,如何使以下内容在现有作业完成之前不会每分钟产生另一个作业?

main :: IO ()
main = do
       tids <- execSchedule $ do
           addJob doBackup "* * * * *"
       print tids

doBackup :: IO ()
doBackup = do
    putStrLn "Backing up system..."
    threadDelay 70000000
    putStrLn "Backup finished"

这是@Alec 建议的实现。这会创建一个 MVar ()(基本上是一个信号量),然后每次您的作业运行时,它都会在继续之前检查 MVar 是否空闲,否则会死亡。

import Control.Concurrent.MVar
import Control.Monad

main :: IO ()
main = do sem <- newMVar () -- starts off not taken
          tids <- execSchedule $ addJob (doBackup sem) "* * * * *" -- do x = x
          print tids
          forever $ threadDelay maxBound
       -- all threads exit when main exits. ^ stops that from happening
       -- Control.Monad.forever x = let loop = x >> loop in loop
       -- don't do forever $ return () unless you want a CPU-nomming busyloop


doBackup :: MVar () -> IO ()
doBackup sem = do res <- tryTakeMVar sem
               -- gives Nothing if the sem is taken and Just () if it isn't
               -- you DON'T want takeMVar, since that'll block until sem is
               -- free, essentially queueing the jobs up instead of
               -- canceling the ones that run too early.
                  case res of
                       Nothing -> return () -- already taken? do nothing
                       Just () -> do putStrLn "Backing up system..."
                                     threadDelay 70000000
                                     putStrLn "Backup finished"
                                     putMVar sem () -- release the lock

仅供参考:这不是异常安全的。有关该方向的指针,请参阅 withMVar 的实现。