一個有用的python裝飾器 -- 爲執行程序加鎖

   最近在寫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是「勸告鎖」,系統內核不會強制檢查鎖的狀態,須要在代碼中進行文件操做的地方顯式檢查才能生效。

詳情請查看官方文檔:

https://docs.python.org/2/library/fcntl.html#fcntl.flock

相關文章
相關標籤/搜索