2016-09-17 13 views
0

私は複数のスレッドがあります。上記のコードは私にいくつかの非常に奇妙な行動を与えていたthreading.Lock()パフォーマンス上の問題

dispQ = Queue.Queue() 
stop_thr_event = threading.Event() 

def worker (stop_event): 
    while not stop_event.wait(0): 
     try: 
      job = dispQ.get(timeout=1) 
      job.waitcount -= 1 
      dispQ.task_done() 
     except Queue.Empty, msg: 
      continue 

# create job objects and put into dispQ here 
for j in range(NUM_OF_JOBS): 
    j = Job() 
    dispQ.put(j) 

# NUM_OF_THREADS could be 10-20 ish 
running_threads = [] 
for t in range(NUM_OF_THREADS): 
    t1 = threading.Thread(target=worker, args=(stop_thr_event,)) 
    t1.daemon = True 
    t1.start() 
    running_threads.append(t1) 


stop_thr_event.set() 
for t in running_threads: 
    t.join() 

。 私はそれが私が「

が、私はこれが思わ

with job.thr_lock: 
    job.waitcount -= 1 

にそれを変更しましたself.thr_lock = threading.Lock() 、ジョブクラスに属性を追加しましロックアウトでwaitcountを減少によるものであったことを見つける終わってきました奇妙な動作を修正するが、性能が低下しているように見える。

これは予想されますか?ロックを最適化する方法はありますか?
ジョブオブジェクトごとに1つのロックではなく、1つのグローバルロックを持つ方がよいでしょうか?

+0

'job.waitcount'の設定または増分は何ですか? – martineau

+0

ところで、あなたは 'except Queue.Empty as msg:'を使うべきです。カンマで書かれた構文はpython2でのみ動作し、 'as'はpython2.6、2.7、3+で動作します。また、複数のタイプの例外をキャッチしたい場合は、他の言語と一貫しています。 – Bakuriu

答えて

1

スレッディングを「最適化」する唯一の方法は、同時に実行できるブロックまたはチャンクで処理を中断する方法です。これは、インタープリタがグローバルインタープリタロック(別名GIL)をリリースする唯一の時間なので、ほとんどは入力または出力(I/O)を行うことを意味します。

実際には、上記の条件が満たされない限り、スレッドを使用する際のオーバーヘッドのためにスレッディングが追加されると、ゲインが低下したり、ネットがスローダウンすることさえあります。

すべての共有リソースに対して1つのグローバルロックを使用した場合、プログラムの一部が、必要なリソースを区別できないため、実際には必要ないときに待機するため、おそらく悪化します不要な待機が発生します。

PyCon 2015の話David Beasleyが、Python Concurrency From the Ground Upというタイトルのタイトルをつけているかもしれません。スレッド、イベントループ、コルーチンを扱います。

+0

は、パフォーマンス面でロックを使用することによる既知の影響はありますか? – ealeon

+0

複数のロックが使用されている場合、それ以上の影響はありますか? – ealeon

+0

(シングルスレッド)テストプログラムで 'timeit'を使用して' Lock'を取得して解放するのにかかる時間を測定できます。これは、それがしている影響を教えてくれるでしょう。それは、それがどれほど頻繁に行われるかによって乗算されなければならないでしょう。ただし、同時に複数のスレッドが排他アクセスを取得しようとした場合、または各スレッドによって保持される可能性のあるスレッドが他のスレッドを実行できないようにするために待機する必要はありません。 – martineau

1

あなたのコードに基づいて質問に答えるのは難しいです。ロックにはいくつかの固有のコストがあり、何も無料ではありませんが、通常は非常に小さくなります。あなたの仕事が非常に小さい場合、それらを「チャンク」することが考えられます。つまり、スレッドごとに行われる作業の量に比べて取得/解放の呼び出しが少なくなります。

関連しているが別個の問題は、お互いをブロックしているスレッドの1つです。多くのスレッドが同じロックを待機している場合、パフォーマンスの問題が発生する可能性があります。ここであなたのスレッドはお互いに待っています。パフォーマンスのボトルネックとなる共有リソースがあるため、これを避けることはできません。他のケースでは、コードを再編成してこのパフォーマンス上の不利益を回避することができます。

サンプルコードには、実際のアプリケーションと大きく異なる可能性のあるものがいくつかあります。まず、サンプルコードはスレッド間でジョブオブジェクトを共有しません。ジョブオブジェクトを共有していない場合は、ロックを解除する必要はありません。第2に、記述されているように、コードは終了する前にキューを空にしないかもしれません。 stop_thr_event.set()が残りのジョブをキューに残すとすぐに終了しますが、これは設計上のものですか?

関連する問題