pylruをマルチスレッドで使うと「KeyError:」が発生する
githubにissueがないのでここに書く。
以下のような2スレッドで同時にlrucacheに書き込むとエラーが起きる。
test.py
import pylru import threading import random cache = pylru.lrucache(1024) def f(): while True: key = 'key %s' % (random.randint(1,100000)) val = 'value' cache[key] = val def main(): th_1 = threading.Thread(target=f, name="th_1") th_2 = threading.Thread(target=f, name="th_2") th_1.start() th_2.start() if __name__ == '__main__': main()
version 1.0.3の場合
$ python test.py Exception in thread th_1: Traceback (most recent call last): File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner self.run() File "/usr/lib64/python2.6/threading.py", line 484, in run self.__target(*self.__args, **self.__kwargs) File "test.py", line 11, in f cache[key] = val File "/usr/lib/python2.6/site-packages/pylru-1.0.3-py2.6.egg/pylru.py", line 145, in __setitem__ del self.table[node.key] KeyError: 'key 41240'
version 1.0.9の場合
$ python test.py Exception in thread th_1: Traceback (most recent call last): File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner self.run() File "/usr/lib64/python2.6/threading.py", line 484, in run self.__target(*self.__args, **self.__kwargs) File "test.py", line 11, in f cache[key] = val File "/usr/lib/python2.6/site-packages/pylru-1.0.9-py2.6.egg/pylru.py", line 145, in __setitem__ del self.table[node.key] KeyError: 'key 82904'
ロックをかければ問題が発生しなくなる。 ただロックが大きすぎると性能がでなくなるわけでどう修正したらベストだろうか。明日以降で取り組む。
2016/12/12 追記
Python 3.2からlru_cacheが標準で提供されているので、おとなしく2系から3系に乗り換えることにしよう。 それまではワークアラウンドで。 10.2. functools — 高階関数と呼び出し可能オブジェクトの操作 — Python 3.6.5 ドキュメント functoolsの実装を見るとRLockをつかっているようだ。