linux的crontab命令,可以定時執行操作,最小周期是每分鐘執行一次。關於crontab實現每秒執行可參考我之前的文章《linux crontab 實現每秒執行》
現在有個問題,如果設定了任務每分鐘執行一次,但有可能一分鐘內任務並沒有執行完成,這時系統會再執行任務。導致兩個相同的任務在執行。
例如:
<? // test.php for($i=0; $i<300; $i++){ echo date('Y-m-d H:i:s')."\r\n"; sleep(1); } ?>
循環300次,每循環一次睡眠1秒。執行完成需要300秒即5分鐘。
設置crontab 為每分鐘執行
* * * * * php /home/fdipzone/php/test.php >> /home/fdipzone/php/test.log
2分鐘後,使用 ps aux|grep test.php 查看,可以看到有兩個test.php進程在執行。
3分鐘後,看到有3個test.php進程在執行。
fdipzone@ubuntu:/tmp$ ps aux|grep test.php fdipzone 2995 0.0 0.0 4220 588 ? Ss 00:28 0:00 /bin/sh -c php /home/fdipzone/php/test.php >> /home/fdipzone/php/test.log fdipzone 2996 0.0 0.8 108328 8564 ? S 00:28 0:00 php /home/fdipzone/php/test.php fdipzone 3033 0.0 0.0 4220 584 ? Ss 00:29 0:00 /bin/sh -c php /home/fdipzone/php/test.php >> /home/fdipzone/php/test.log fdipzone 3034 0.1 0.8 108328 8564 ? S 00:29 0:00 php /home/fdipzone/php/test.php fdipzone 3047 0.0 0.0 4220 588 ? Ss 00:30 0:00 /bin/sh -c php /home/fdipzone/php/test.php >> /home/fdipzone/php/test.log fdipzone 3048 1.3 0.8 108328 8560 ? S 00:30 0:00 php /home/fdipzone/php/test.php fdipzone 3051 0.0 0.1 13148 1068 pts/0 S+ 00:30 0:00 grep --color=auto test.php
我們是希望執行完上一任務,再執行下一任務,如果上一任務未執行完成,則這次的任務不執行,直到下一周期再判斷,如果上一任務執行完成,則可以執行下一任務。
改進方法
我們可以使用一個鎖文件,來記錄任務是否執行中。
首先判斷/tmp/mytest.lock是否存在,如果不存在,則創建,然後執行任務,任務執行完後刪除鎖文件。
如果鎖文件已經存在,則退出這次的任務。
<?php $lockfile = '/tmp/mytest.lock'; if(file_exists($lockfile)){ exit(); }else{ file_put_contents($lockfile, 1, true); } for($i=0; $i<300; $i++){ echo date('Y-m-d H:i:s')."\r\n"; sleep(1); } unlink($lockfile); ?>
這樣的確可以保證任務執行其間不會有新任務執行,但這樣需要在任務文件中寫代碼做判斷,不方便。能不能把任務鎖定的判斷放在任務以外呢?
使用linux flock 文件鎖實現任務鎖定,解決沖突
格式:
flock [-sxun][-w #] fd#
flock [-sxon][-w #] file [-c] command
選項
-s, --shared: 獲得一個共享鎖 -x, --exclusive: 獲得一個獨占鎖 -u, --unlock: 移除一個鎖,通常是不需要的,腳本執行完會自動丟棄鎖 -n, --nonblock: 如果沒有立即獲得鎖,直接失敗而不是等待 -w, --timeout: 如果沒有立即獲得鎖,等待指定時間 -o, --close: 在運行命令前關閉文件的描述符號。用於如果命令產生子進程時會不受鎖的管控 -c, --command: 在shell中運行一個單獨的命令 -h, --help 顯示幫助 -V, --version: 顯示版本
繼續用回第一個test.php,文件鎖使用獨占鎖,如果鎖定則失敗不等待。參數為-xn
* * * * * flock -xn /tmp/mytest.lock -c 'php /home/fdipzone/php/test.php >> /home/fdipzone/php/test.log'
這樣當任務未執行完成,下一任務判斷到/tmp/mytest.lock被鎖定,則結束當前的任務,下一周期再判斷。
本欄目更多精彩內容:http://www.bianceng.cn/OS/Linux/