2010-02-09 54 views
20

我需要定期運行Perl腳本(〜每3-5分鐘)。我想確保一次只能運行一個Perl腳本實例,所以下一個循環在上一個循環完成之前不會啓動。可以/應該通過cron,Perl的某些內置功能來實現,還是我需要在腳本級別處理它?由cron運行只有一個Perl腳本實例

我對Perl和cron相當陌生,所以需要幫助和一般建議。

+0

哪個平臺,你在說什麼? * nix中? – weismat 2010-02-09 22:27:46

+0

它會在Linux上 – 2010-02-09 22:32:31

+0

http://blog.booking.com/highlander-daemons-without-daemons.html – stu42j 2014-04-18 17:22:21

回答

14

我一直使用File::NFSLock得到的腳本本身的獨佔鎖好運。

use Fcntl qw(LOCK_EX LOCK_NB); 
use File::NFSLock; 

# Try to get an exclusive lock on myself. 
my $lock = File::NFSLock->new($0, LOCK_EX|LOCK_NB); 
die "$0 is already running!\n" unless $lock; 

這與其他鎖文件建議的排序方式相同,只是除了嘗試獲取鎖之外,沒有其他任何操作。

+0

如果腳本沒有寫入權限,這是否工作? – mob 2010-02-10 05:45:45

+0

謝謝!我最喜歡它,因爲它非常緊湊,不需要寫任何文件。 – 2010-02-10 09:16:57

+1

它仍在使用鎖定文件。如果腳本是foo.pl,則默認使用foo.pl.NFSLock。所以你需要對目錄有寫權限。 – oylenshpeegul 2010-02-10 13:01:30

1

AFAIK perl沒有這樣的東西buildin。當您啓動應用程序並刪除它時,您可以輕鬆創建一個臨時文件,當您的腳本完成時。

+2

對不起,我預見帶來很大麻煩,如果出現錯誤(機器重新啓動)和文件不會被刪除運行... – 2010-02-09 22:13:02

+0

公平地說,但是如果機器在腳本運行時重新啓動,那麼您可能會遇到麻煩(除非腳本做了一些非常微不足道的事情)。 – 2010-02-09 22:33:07

+0

在腳本運行期間可能發生重新啓動。該腳本使用數據庫事務,所以我認爲它會「重新生存」重新啓動,以保持數據的完整性。 – 2010-02-09 22:40:58

-1

鑑於頻率,我通常會編寫一個守護進程(服務器),在作業運行之間很好地等待(即sleep()),而不是嘗試使用cron進行相當精細的訪問。

如果有必要,在Unix/Linux系統上,您可以從/etc/inittab(或更換)運行它,以確保它始終運行,並且在進程死亡或死亡時自動重啓。

新增:(和一些無關緊要的東西去掉)

總是存在的(運行,但大多是空閒的)進程的方法具有消除腳本的併發實例的可能性的好處正在被由cron自動啓動。

但是,這意味着您有責任正確管理計時,例如在存在重疊的情況下(例如,上次運行仍在運行,而新的觸發器發生)。這可以幫助您決定是使用分叉守護進程還是非分叉設計。線程在這種情況下不提供任何優勢,因此不需要考慮它們的用法。

這並不能完全消除多進程運行的可能性,但這是許多守護進程的常見問題。典型的解決方案是在文件上使用諸如互斥鎖的信號量,以防止運行第二個實例。當進程結束時,文件鎖自動被遺忘,因此在異常終止(例如電源故障)的情況下,鎖自身不需要清理。

使用FCNTL模塊,以及使用一個Perl sysopenO_EXCL標誌(或O_RDWR | O_CREAT | O_EXCL)一種方法是given by Greg Bacon。我唯一的區別是將獨佔鎖定結合到sysopen調用中(即使用我建議的標誌),然後刪除當時多餘的調用。哦,我會遵循UNIX(& Linux FHS)文件系統和命名約定/var/run/daemonname.pid

另一種方法是使用djb的daemontoolssimilar來「守護」任務。

+0

運行守護進程並不能消除多個運行腳本問題..對於某人或某事偶然嘗試啓動第二個守護進程很容易。你仍然需要一個PID文件來處理這個問題。 – mpounsett 2015-02-25 21:48:59

3

一個典型的方法是每個進程打開並鎖定某個文件。然後該進程讀取文件中包含的進程ID。

如果使用該ID的進程正在運行,那麼後來者安靜地退出。否則,新獲勝者將其進程ID(Perl中的$$)寫入pidfile,關閉句柄(釋放鎖)並執行其業務。

舉例如下實施:

#! /usr/bin/perl 

use warnings; 
use strict; 

use Fcntl qw/ :DEFAULT :flock :seek /; 

my $PIDFILE = "/tmp/my-program.pid"; 
sub take_lock { 
    sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT or die "$0: open $PIDFILE: $!"; 
    flock $fh => LOCK_EX      or die "$0: flock $PIDFILE: $!"; 

    my $pid = <$fh>; 
    if (defined $pid) { 
    chomp $pid; 
    if (kill 0 => $pid) { 
     close $fh; 
     exit 1; 
    } 
    } 
    else { 
    die "$0: readline $PIDFILE: $!" if $!; 
    } 

    sysseek $fh, 0, SEEK_SET or die "$0: sysseek $PIDFILE: $!"; 
    truncate $fh, 0   or die "$0: truncate $PIDFILE: $!"; 
    print $fh "$$\n"  or die "$0: print $PIDFILE: $!"; 
    close $fh    or die "$0: close: $!"; 
} 

take_lock; 
print "$0: [$$] running...\n"; 
sleep 2; 
11

使用File::Pid存儲腳本的PID的文件,該腳本應在開始時檢查,如果發現異常中止。當劇本完成後,你可以刪除pidfile進程文件,但它不是真正需要的,你可以簡單地檢查後,看是否該進程ID仍然活着(這也將佔到情況下,當你的腳本中意外中斷):

use strict; 
use warnings; 
use File::Pid; 

my $pidfile = File::Pid->new({file => /var/run/myscript}); 
exit if $pidfile->running(); 

$pidfile->write(); 

# ... rest of script... 

# end of script 
$pidfile->remove(); 
exit; 
+0

我很驚訝地發現沒有雞羣來源! – 2010-02-09 22:44:37

+0

@gbacon:隨時提交補丁:) – Ether 2010-02-09 22:58:07

+0

我不會推薦這個答案。你有一個競爭條件(運行後調用write)。這可能永遠不會成爲cron工作中的一個問題,但仍然可以避免,因爲有更好的答案(請參閱已接受的答案)。 – Calimo 2016-06-17 07:36:20

11

Sys :: RunAlone模塊可以很好地完成你想要的功能。只需添加

use Sys::RunAlone; 

靠近代碼頂部。

+0

+1。 – moodboom 2015-02-03 22:54:00

1

我一直使用這個 - 小而簡單 - 不依賴於任何模塊,並且可以同時運行Windows + Linux。

use Fcntl ':flock';      

### Check to make sure there is only one instance ### 
open SELF, "< $0" or die("Cannot run two instances of this program"); 
unless (flock SELF, LOCK_EX | LOCK_NB) { 
    print "You cannot run two instances of this program , a process is still running"; 
    exit 1; 
}