我正在尝试学习Haskell。 首先,我希望编写一个定期为我运行备份作业的守护进程。
我正在使用System.Cron.Schedule
为我进行计划,并运行restic
可执行文件进行备份。
由于这是一个备份作业,因此运行可能需要日志时间,为了防止在上一个备份尚未完成时运行下一个计划备份,我想确保我只能运行作业的 1 个实例。 如何防止长时间运行的作业再次运行?
举个简化的例子,在现有作业完成之前,如何使以下内容不会每分钟生成另一个作业?
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
的实现。