読者です 読者をやめる 読者になる 読者になる

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.5.2 ドキュメント functoolsの実装を見るとRLockをつかっているようだ。