複数のスレッド(_threading.Thread
_で作成)がQueue
からqueue.get_nowait()
を使用してURLをフェッチし、HTMLを処理するスクリプトを作成しました。私はマルチスレッドプログラミングに不慣れで、queue.task_done()
関数の目的を理解できません。
Queue
が空の場合、_queue.Empty
_例外が自動的に返されます。したがって、各スレッドがtask_done()
関数を呼び出す必要性を理解していません。キューが空になったときにキューが完了したことがわかっているので、ワーカースレッドが作業を完了したことを通知する必要があるのはなぜですか(URLを取得した後は、キューとは関係ありません)。 ?
この関数が実際のアプリケーションでどのように使用されるかを示すコード例(理想的にはurllib
、ファイルI/O、またはフィボナッチ数以外の何かを使用し、「Hello」を出力する)を誰かに教えてもらえますか?
Queue.task_done
は、労働者の利益のためにありません。 Queue.join
をサポートするためにあります。
仕事の割り当ての箱を渡した場合、箱からすべてを取り出したのはいつですか。
いいえ。私は作業がいつ終了するかを気にします。空の箱を見てもわかりません。あなたと他の5人の人が、箱から取り出したものにまだ取り組んでいる可能性があります。
Queue.task_done
を使用すると、タスクが完了したときに労働者が言うことができます。 Queue.join
ですべての作業が完了するのを待っている人は、キューが空でないときではなく、十分なtask_done
の呼び出しが行われるまで待機します。
誰かが私にこの関数が実際のアプリケーションでどのように使用されるかを示すコード例(理想的にはurllib、ファイルI/O、またはフィボナッチ数以外のものを使用し、「Hello」を出力する)を提供できますか?
@ user2357112の answer はtask_done
の目的をうまく説明していますが、要求された例が欠けています。これは、任意の数のファイルのチェックサムを計算し、各ファイル名を対応するチェックサムにマッピングするdictを返す関数です。関数の内部では、作業はいくつかのスレッドに分割されます。
関数はQueue.join
を使用して、ワーカーが割り当てられたタスクを完了するまで待機するため、呼び出し元にディクショナリを安全に返すことができます。これは、単にデキューされるのではなく、すべてのファイルが処理されるのを待つのに便利な方法です。
import threading, queue, hashlib
def _work(q, checksums):
while True:
filename = q.get()
if filename is None:
q.put(None)
break
try:
sha = hashlib.sha256()
with open(filename, 'rb') as f:
for chunk in iter(lambda: f.read(65536), b''):
sha.update(chunk)
checksums[filename] = sha.digest()
finally:
q.task_done()
def calc_checksums(files):
q = queue.Queue()
checksums = {}
for i in range(1):
threading.Thread(target=_work, args=(q, checksums)).start()
for f in files:
q.put(f)
q.join()
q.put(None) # tell workers to exit
return checksums
GILに関するメモ:hashlib
のコードはチェックサムの計算中に内部的にGILを解放するため、複数のスレッドを使用すると測定可能になります(Pythonバージョンに応じて1.75x-2x)シングルスレッドのバリアントと比較してスピードアップ。
.task_done()
は、処理が完了したことを.join()
にマークするために使用されます。
????
.join()
を使用し、処理されたすべてのアイテムに対して.task_done()
を呼び出さない場合、スクリプトは永久にハングします。
短い例のようではありません。
import logging
import queue
import threading
import time
items_queue = queue.Queue()
running = False
def items_queue_worker():
while running:
try:
item = items_queue.get(timeout=0.01)
if item is None:
continue
try:
process_item(item)
finally:
items_queue.task_done()
except queue.Empty:
pass
except:
logging.exception('error while processing item')
def process_item(item):
print('processing {} started...'.format(item))
time.sleep(0.5)
print('processing {} done'.format(item))
if __name__ == '__main__':
running = True
# Create 10 items_queue_worker threads
worker_threads = 10
for _ in range(worker_threads):
threading.Thread(target=items_queue_worker).start()
# Populate your queue with data
for i in range(100):
items_queue.put(i)
# Wait for all items to finish processing
items_queue.join()
running = False
「ソースを読んで、ルーク!」 -オビワンコドビ
ayncio.queue のソースはかなり短いです。
これにより、task_done()を呼び出す場合にのみ、joinが役立ちます。古典的な銀行の例えを使用する:
Task_done()がないと、すべての窓口係が人で処理されたことを知ることはできません。窓口に人がいる間は、出納係を家に送ることはできません。