一个有用的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

相关文章
相关标签/搜索