web-dev-qa-db-ja.com

Python-queue.task_done()は何に使用されますか?

複数のスレッド(_threading.Thread_で作成)がQueueからqueue.get_nowait()を使用してURLをフェッチし、HTMLを処理するスクリプトを作成しました。私はマルチスレッドプログラミングに不慣れで、queue.task_done()関数の目的を理解できません。

Queueが空の場合、_queue.Empty_例外が自動的に返されます。したがって、各スレッドがtask_done()関数を呼び出す必要性を理解していません。キューが空になったときにキューが完了したことがわかっているので、ワーカースレッドが作業を完了したことを通知する必要があるのはなぜですか(URLを取得した後は、キューとは関係ありません)。 ?

この関数が実際のアプリケーションでどのように使用されるかを示すコード例(理想的にはurllib、ファイルI/O、またはフィボナッチ数以外の何かを使用し、「Hello」を出力する)を誰かに教えてもらえますか?

11
J. Taylor

Queue.task_doneは、労働者の利益のためにありません。 Queue.joinをサポートするためにあります。


仕事の割り当ての箱を渡した場合、箱からすべてを取り出したのはいつですか。

いいえ。私は作業がいつ終了するかを気にします。空の箱を見てもわかりません。あなたと他の5人の人が、箱から取り出したものにまだ取り組んでいる可能性があります。

Queue.task_doneを使用すると、タスクが完了したときに労働者が言うことができますQueue.joinですべての作業が完了するのを待っている人は、キューが空でないときではなく、十分なtask_doneの呼び出しが行われるまで待機します。

13
user2357112

誰かが私にこの関数が実際のアプリケーションでどのように使用されるかを示すコード例(理想的にはurllib、ファイルI/O、またはフィボナッチ数以外のものを使用し、「Hello」を出力する)を提供できますか?

@ user2357112の answertask_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)シングルスレッドのバリアントと比較してスピードアップ。

2
user4815162342

.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
2
Jossef Harush

「ソースを読んで、ルーク!」 -オビワンコドビ

ayncio.queue のソースはかなり短いです。

  • キューに入れると、未完了のタスクの数が1つ増えます。
  • task_doneを呼び出すと1つ下がります
  • join()は、未完了のタスクがなくなるまで待機します。

これにより、task_done()を呼び出す場合にのみ、joinが役立ちます。古典的な銀行の例えを使用する:

  • 人々がドアに来て列に並ぶ。 doorはq.put()を実行するプロデューサーです
  • 窓口係がアイドルで人が並んでいるとき、窓口に行きます。 tellerはq.get()を実行します。
  • 窓口係が人の手伝いを終えると、次の人の準備が整います。 tellerはq.task_done()を実行します
  • 午後5時、ドアはロックされますドアタスクの終了
  • 行が空の両方になるまで待ちます。各窓口は、目の前の人の支援を終了します。 待ちますq.join(tellers)
  • 次に、テラーを家に送ります。テラーは、すべて空のキューで待機しています。 窓口係の場合:teller.cancel()

Task_done()がないと、すべての窓口係が人で処理されたことを知ることはできません。窓口に人がいる間は、出納係を家に送ることはできません。

0
Charles Merriam