私のアプリケーションには、ログ行で非常に高速な処理を実行して float 値を生成する 1 つのスレッドがあります。通常、間隔を置いて値の低速読み取りを実行する他のスレッドは 1 つだけです。ときどき、他のスレッドが行き来し、それらの値に対して 1 回限りの読み取りを実行することもあります。
私の質問は、データが単に利用可能な最新のデータであるこの特定のケースに対して、(cpython で) ミューテックスの必要性についてです。他のものと同期する必要がある重要な値ではありません (または同時に書き込まれる他のフィールドでさえも)。ただ単純に... あるときの値は何ですか。
そうは言っても、値の更新を保護するためにロック(またはリーダー/書き込みロック)を簡単に追加できることはわかっていますが、ログ全体の過程で取得/解放のオーバーヘッドが立て続けに発生するのではないかと思います(平均5000行としましょう)は、共有リソースを「適切に」実行するだけの価値はありません。
ドキュメントに基づくスレッドセーフなグローバル値の突然変異の種類は? 、これらの割り当てはアトミック操作である必要があります。
ロジックの基本的な例を次に示します。
import time
from random import random, choice, randint
from threading import Thread
class DataStructure(object):
def __init__(self):
self.f_val = 0.0
self.s_val = ""
def slow_reader(data):
"""
Loop much more slowly and read values
anywhere between 1 - 5 second intervals
"""
for _ in xrange(10):
f_val = data.f_val
# don't care about sync here
s_val = data.s_val
print f_val, s_val
# in real code could be even 30 or 60 seconds
time.sleep(randint(1,3))
def fast_writer(data):
""" Update data extremely often """
for _ in xrange(20000):
f_val, s_val = do_work()
data.f_val = f_val
# don't care about sync here
data.s_val = s_val
FLOAT_SRC = [random()*100 for _ in xrange(100)]
STR_SRC = ['foo', 'bar', 'biz', 'baz']
def do_work():
time.sleep(0.001)
return choice(FLOAT_SRC), choice(STR_SRC)
if __name__ == "__main__":
data = DataStructure()
threads = [
Thread(target=slow_reader, args=(data,)),
Thread(target=fast_writer, args=(data,)),
]
for t in threads:
t.daemon=True
t.start()
for t in threads:
t.join()
これは、高速のログ パーサー (実際には PIPE を介して読み取られる) が各行で作業を行い、低速の定期的なリーダーがその時点で現在の値を取得していることを表します。いつでも、一度読み取られた別のスレッドが行き来して、データ構造から同じ値を取得する可能性があります。
これは、cpython のミューテックスがまったく必要ない状況ですか?
編集
もう少し明確にするために...浮動小数点フィールドと文字列フィールドを最後の書き込みから同期させる必要さえありません。スケジューラーが float 読み取りと string 読み取りの間でコンテキストを切り替えることを決定した場合は問題ありません。いつでも割り当てられている値を単純に読み取るために、ロックのオーバーヘッドが必要かどうか疑問に思っています。
私の懸念は、非常に高速な動作でライターがループし、多くの場合競合しないロックをロックおよびロック解除するという事実に関するものです。
事実上、これが私が気にかけているすべてであると仮定しますreader
:
def slow_reader(data):
for _ in xrange(10):
f_val = data.f_val
print f_val
time.sleep(randint(1,3))