shelve在只读多进程中是否不安全?
shelve
模块的文档在限制部分提到:
shelve
模块不支持对存储对象的同时读写访问。(多个同时的读取是安全的。)
根据我的理解,这意味着只要我不试图让多个进程同时写入同一个存储,就没问题。多个进程只用这个存储来读取数据应该是安全的,对吧?
显然不是。经过一番挣扎,我最终得到了一个测试案例,似乎显示出在异步读取存储时会出现一些非常糟糕的情况。以下是这个脚本:
- 创建一个
Shelf
,并用"i" : 2*i
填充它,i
从1到10。 - 读取所有这些值,以确保它们被正确存储。
启动进程从存储文件中检索每个键的值,并报告是否成功获取到值。
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,如果这有关系的话。
1 个回答
2
在shelve.open()
的说明中,有一个提醒:
这个函数可以打开一个持久化的字典。你指定的文件名是底层数据库的基础文件名。作为一个附带效果,文件名可能会增加一个扩展名,并且可能会创建多个文件。
试着把一个已经打开的shelve(而不是文件名)传给线程池,看看行为是否会有所不同。不过,我在2.7版本和Win7-64上没有复现这个问题(输出当然是乱七八糟的)。