0

クールで簡単な Web クローラーを開発するためにスレッド化について調べていますが、動作が非常に遅いです。

これは、 ibm ライブラリーで見つけたコードの断片です。

    urls = [] # huge list of urls
    in_queue = Queue.Queue()
    out_queue = Queue.Queue()

    pool = ActivePool()
    s = threading.Semaphore(semaphore)

    for url in urls[:slice_size]:
            in_queue.put(url)
            t = ThreadUrl(pool, s, url, in_queue, out_queue)
            t.setDaemon(True)
            t.start()

    counter = slice_size
    while not in_queue.empty() or not out_queue.empty():
          speed_new_daemon = time.time()
          url = urls[counter]
          in_queue.put(url)
          t = ThreadUrl(pool, s, url, in_queue, out_queue)
          t.setDaemon(True)

          t.start()        # <------ why 20% of all time I lose here?

          counter += 1
          speed_new_daemon = time.time() - speed_new_daemon

          speed_parser = time.time()
          result = out_queue.get()
          my_parser(result)
          speed_parser = time.time() - speed_parser

          # speed_parser only 80%, when speed_new_daemon takes 20%...
    in_queue.join()
4

1 に答える 1

4

引用した IBM ドキュメントは、5 つのスレッドを開始します。

#spawn a pool of threads, and pass them queue instance 
for i in range(5):
  t = ThreadUrl(queue)
  t.setDaemon(True)
  t.start()

あなたのコードは完全に異なります。まず、URL ごとに 1 つのスレッドのプールを作成します。

for url in urls[:slice_size]:
    in_queue.put(url)
    t = ThreadUrl(pool, s, url, in_queue, out_queue)
    t.setDaemon(True)
    t.start()

そして、メイン ループを通過するたびに新しいスレッドを作成し続けます。

while not queue.empty() or not out_queue.empty():
      speed_start_new_daemon = time.time()
      url = urls[counter]
      in_queue.put(url)
      t = ThreadUrl(pool, s, url, in_queue, out_queue)
      t.setDaemon(True)

そのため、最終的には大量のスレッドが作成されることになります。これらすべてのスレッドが数個のコアのみをめぐって競合するため、各スレッドはほとんどの時間を待機に費やします。その上、最終的にはスケジューラに負荷をかけることになるため、CPU はスレッドの実行方法を理解するためにかなりの時間を費やすことになります。

一方、1000 個のスレッドを開始してもそれほど CPU 時間はかからないかもしれませんが、1000 個の URL をダウンロードするのにもそれほど CPU 時間はかかりません (I/O バウンドであり、プロセッサ バウンドではありません)。濃縮?

于 2013-07-10T00:26:55.470 に答える