How to prevent a race condition when multiple processes attempt to write to and then read from a file at the same time
我有以下代码(为了清楚起见,简化了代码):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import os import errno import imp lib_dir = os.path.expanduser('~/.brian/cython_extensions') module_name = '_cython_magic_5' module_path = os.path.join(lib_dir, module_name + '.so') code = 'some code' have_module = os.path.isfile(module_path) if not have_module: pyx_file = os.path.join(lib_dir, module_name + '.pyx') # THIS IS WHERE EACH PROCESS TRIES TO WRITE TO THE FILE. THE CODE HERE # PREVENTS A RACE CONDITION. try: fd = os.open(pyx_file, os.O_CREAT | os.O_EXCL | os.O_WRONLY) except OSError as e: if e.errno == errno.EEXIST: pass else: raise else: os.fdopen(fd, 'w').write(code) # THIS IS WHERE EACH PROCESS TRIES TO READ FROM THE FILE. CURRENTLY THERE IS A # RACE CONDITION. module = imp.load_dynamic(module_name, module_path) |
(上面的一些代码是从这个答案中借用的。)
当同时运行多个进程时,此代码只会导致一个进程打开并写入
避免这些错误的最佳方法是什么?一种想法是让进程在一段时间内一直尝试导入
这样做的方法是每次打开一个专用锁。写入程序在写入数据时持有锁,而读卡器将一直阻塞,直到写入程序使用fdclose调用释放锁为止。当然,如果文件已部分写入并且写入过程异常退出,这将失败,因此,如果模块无法加载,则应显示删除文件的适当错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import os import fcntl as F def load_module(): pyx_file = os.path.join(lib_dir, module_name + '.pyx') try: # Try and create/open the file only if it doesn't exist. fd = os.open(pyx_file, os.O_CREAT | os.O_EXCL | os.O_WRONLY): # Lock the file exclusively to notify other processes we're writing still. F.flock(fd, F.LOCK_EX) with os.fdopen(fd, 'w') as f: f.write(code) except OSError as e: # If the error wasn't EEXIST we should raise it. if e.errno != errno.EEXIST: raise # The file existed, so let's open it for reading and then try and # lock it. This will block on the LOCK_EX above if it's held by # the writing process. with file(pyx_file,"r") as f: F.flock(f, F.LOCK_EX) return imp.load_dynamic(module_name, module_path) module = load_module() |
每次访问文件时,使用pid->strike>an empty file to lock every you access a file.
示例用法:
1 2 3 4 5 6 7 8 9 | from mercurial import error, lock try: l = lock.lock("/tmp/{0}.lock".format(FILENAME), timeout=600) # wait at most 10 minutes # do something except error.LockHeld: # couldn't take the lock else: l.release() |
源:python:用于创建基于pid的锁文件的模块?
这会给你一个大概的想法。这种方法被用于OO、VIM和其他应用中。