最近在寫python項目的時候遇到一個問題html
有這樣一個python腳本:python
1 腳本功能 linux
A 監控網卡的實時流量,若是該流量超過設定閥值就去增長帶寬(購買帶寬包)windows
B 腳本放在 crontab 中,而且是每分鐘執行一次ide
2 遇到問題函數
在執行腳本的過程當中,若是一分鐘內該程序沒有執行完,就可能會有兩個相同的代碼同時執行,致使的問題是 會同時購買兩次帶寬包,這樣會形成資源的浪費。spa
3 解決思路orm
A 能夠調整crontab,增長代碼執行的時間(治標不治本)htm
B 爲該程序加鎖,同一時間只容許運行一個監控程序。進程
4 解決步驟
所以,怎麼爲程序加鎖,還要儘可能減小對源代碼的更改,是問題的關鍵,我這裏使用到了python的裝飾器。
思路是這樣的:
A 打開一個 xx.pid 文件,併爲這個文件加上鎖
B 獲取當前執行程序的PID,並寫入到 xx.pid文件中
C 執行程序,執行結束後關閉而且刪除該xx.pid文件
5 上代碼
#!/usr/bin/python # -*- coding: utf-8 -*- import fcntl from functools import wraps import os def singleton(pid_filename): def decorator(f): @wraps(f) def decorated(*args, **kwargs): pid = str(os.getpid()) pidfile = open(pid_filename, 'a+') try: #建立一個排他鎖,而且所被鎖住其餘進程不會阻塞 fcntl.flock(pidfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: return pidfile.seek(0) pidfile.truncate() # 清空文件 pidfile.write(pid) pidfile.flush() pidfile.seek(0) ret = f(*args, **kwargs) try: pidfile.close() except IOError, err: # 可能的錯誤 IOError: [Errno 9] Bad file descriptor if err.errno != 9: return os.remove(pid_filename) return ret return decorated return decorator
注意:
fcntl 僅在 linux系統中使用,在windows中無效
6 簡單用例
@singleton('/tmp/add_bandwidth.pid') def add_bandwidth(): pass
7 代碼解析
這裏使用到了python 的fcntl模塊 -- python中給文件加鎖
簡單示例:
>>> import fcntl >>> f = open('test.txt') >>> fcntl.flock(f.fileno(),fcntl.LOCK_EX) #對文件加鎖 >>> fcntl.flock(f.fileno(),fcntl.LOCK_UN) #對文件解鎖
----------------------
fcntl模塊 中的flock(fd, operation):
參數 fd 表示文件描述符;
參數 operation 指定要進行的鎖操做,該參數的取值有以下幾種:
operation : 包括:
fcntl.LOCK_UN 解鎖
fcntl.LOCK_EX 排他鎖
fcntl.LOCK_SH 共享鎖
fcntl.LOCK_NB 非阻塞鎖
LOCK_SH 共享鎖:全部進程沒有寫訪問權限,即便是加鎖進程也沒有。全部進程有讀訪問權限。
LOCK_EX 排他鎖:除加鎖進程外其餘進程沒有對已加鎖文件讀寫訪問權限。
LOCK_NB 非阻塞鎖:若是指定此參數,函數不能得到文件鎖就當即返回,不然,函數會等待得到文件鎖。
LOCK_NB能夠同LOCK_SH或LOCK_NB進行按位或(|)運算操做。 fcnt.flock(f,fcntl.LOCK_EX|fcntl.LOCK_NB)
注意:
1. 對於文件的 close() 操做會使文件鎖失效;
2. 同理,進程結束後文件鎖失效;
3. flock() 的 LOCK_EX是「勸告鎖」,系統內核不會強制檢查鎖的狀態,須要在代碼中進行文件操做的地方顯式檢查才能生效。
詳情請查看官方文檔: