最近在写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是“劝告锁”,系统内核不会强制检查锁的状态,须要在代码中进行文件操做的地方显式检查才能生效。
详情请查看官方文档: