shelve
模块的文档根据限制 做出以下声明:
The shelve module does not support concurrent read/write access to shelved objects. (Multiple simultaneous read accesses are safe.)
据我所知,这意味着只要我不尝试让多个进程同时写入一个 shelf,我应该是安全的。使用同一个架子作为只读缓存的多个进程应该是安全的。对吧?
显然不是。经过一番努力,我最终得到了一个测试用例,该用例似乎在从货架上异步读取时展示了一些非常糟糕的行为。以下脚本:
- 创建一个
Shelf
并用"i": 2*i
填充i
从 1 到 10。 - 读回所有这些值,以确保它们被正确存储。
Spawns 进程从 shelf 文件中检索每个键的值,并报告是否检索到值。
import multiprocessing import shelve SHELF_FILE = 'test.shlf' def store(key, obj): db = shelve.open(SHELF_FILE, 'w') db[key] = obj db.close() def load(key): try: db = shelve.open(SHELF_FILE, 'r') n = db.get(key) if n is not None: print('Got result {} for key {}'.format(n, key)) else: print('NO RESULT for key {}'.format(key)) except Exception as e: print('ERROR on key {}: {}'.format(key, e)) finally: db.close() if __name__ == '__main__': db = shelve.open(SHELF_FILE, 'n') # Create brand-new shelf db.close() for i in range(1, 11): # populate the new shelf with keys from 1 to 10 store(str(i), i*2) db = shelve.open(SHELF_FILE, 'r') # Make sure everything got in there. print(', '.join(key for key in db)) # Should print 1-10 in some order db.close() # read each key's value from the shelf, asynchronously pool = multiprocessing.Pool() for i in range(1, 11): pool.apply_async(load, [str(i)]) pool.close() pool.join()
这里的预期输出自然是 2, 4, 6, 8
等等,直到 20(按某种顺序)。相反,无法从货架上检索任意值,有时请求会导致 shelve
完全崩溃。实际输出如下所示:(“NO RESULT”行表示返回 None
的键):
6, 7, 4, 5, 2, 3, 1, 10, 8, 9
ERROR on key 3: need 'c' or 'n' flag to open new db
ERROR on key 6: need 'c' or 'n' flag to open new db
Got result 14 for key 7
NO RESULT for key 10
Got result 2 for key 1
Got result 4 for key 2
NO RESULT for key 8
NO RESULT for key 4
NO RESULT for key 5
NO RESULT for key 9
根据错误消息,我的直觉是外部资源(可能是 .dir
文件?)没有正确刷新到磁盘(或者它们可能被其他进程删除了? ).即便如此,我预计在进程等待磁盘资源时速度会变慢,而不是出现“哦,我猜它不在那里”或“你在说什么,这甚至不是一个搁置文件”的结果。坦率地说,我不希望对这些文件进行任何写入,因为工作进程仅使用只读连接...
我是否遗漏了什么,或者 shelve
在多处理环境中完全无法使用?
这是 Windows 7 上的 Python 3.3 x64,如果相关的话。
最佳答案
shelve.open()
文档中有警告注释:
Open a persistent dictionary. The filename specified is the base filename for the underlying database. As a side-effect, an extension may be added to the filename and more than one file may be created.
尝试将预先打开的搁置(而不是文件名)传递给池线程,并查看行为是否发生变化。就是说,我没有 2.7、Win7-64 的重现(输出当然是一团糟)。
关于python - 搁置只读多处理不安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23397979/