6

URLを複数回フェッチする必要がある小さなクローラーを作成しています。すべてのスレッドを同時に(同時に)実行する必要があります。

私はそれを行うべき小さなコードを書きました。

import thread
from urllib2 import Request, urlopen, URLError, HTTPError


def getPAGE(FetchAddress):
    attempts = 0
    while attempts < 2:
        req = Request(FetchAddress, None)
        try:
            response = urlopen(req, timeout = 8) #fetching the url
            print "fetched url %s" % FetchAddress
        except HTTPError, e:
            print 'The server didn\'t do the request.'
            print 'Error code: ', str(e.code) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        except URLError, e:
            print 'Failed to reach the server.'
            print 'Reason: ', str(e.reason) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        except Exception, e:
            print 'Something bad happened in gatPAGE.'
            print 'Reason: ', str(e.reason) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        else:
            try:
                return response.read()
            except:
                "there was an error with response.read()"
                return None
    return None

url = ("http://www.domain.com",)

for i in range(1,50):
    thread.start_new_thread(getPAGE, url)

apacheログからは、スレッドが同時に実行されているようには見えません。リクエスト間に少しギャップがあり、ほとんど検出できませんが、スレッドが実際には並列ではないことがわかります。

GILについて読んだことがありますが、C \ C ++コードを呼び出さずにGILをバイパスする方法はありますか?GILでどのようにスレッド化が可能か本当に理解できませんか?Pythonは基本的に、前のスレッドで終了するとすぐに次のスレッドを解釈しますか?

ありがとう。

4

5 に答える 5

6

ご指摘のとおり、GILはPythonスレッドの並列実行を妨げることがよくあります。

ただし、常にそうであるとは限りません。1つの例外は、I/Oバウンドコードです。スレッドがI/O要求の完了を待機している場合、通常、待機に入る前にGILを解放します。これは、他のスレッドがその間に進行できることを意味します。

ただし、一般的に、multiprocessing真の並列処理が必要な場合は、より安全な方法です。

于 2011-09-09T12:58:40.540 に答える
1

このようなアプローチを使用して、すべてのスレッドを作成し、条件オブジェクトを待機させてから、「同時に」 URLのフェッチを開始させることができます。

#!/usr/bin/env python
import threading
import datetime
import urllib2

allgo = threading.Condition()

class ThreadClass(threading.Thread):
    def run(self):
        allgo.acquire()
        allgo.wait()
        allgo.release()
        print "%s at %s\n" % (self.getName(), datetime.datetime.now())
        url = urllib2.urlopen("http://www.ibm.com")

for i in range(50):
    t = ThreadClass()
    t.start()

allgo.acquire()
allgo.notify_all()
allgo.release()

これにより、すべてのフェッチが同時に発生するようになりますが、:

  • コンピュータを離れるネットワークパケットは、同時にではなく、順番にイーサネットワイヤを通過します。
  • マシンに16以上のコアがある場合でも、マシンとWebホストの間にあるルーター、ブリッジ、モデム、またはその他の機器はコアが少ない可能性があり、リクエストをシリアル化する可​​能性があります。
  • ものをフェッチしているWebサーバーは、accept()呼び出しを使用して要求に応答します。正しい動作を実現するには、サーバーグローバルロックを使用して実装し、1つのサーバープロセス/スレッドのみがクエリに応答するようにします。一部のリクエストが同時にサーバーに到着した場合でも、これによりシリアル化が発生します。

おそらく、リクエストがより多く重複するようになります(つまり、他のリクエストが終了する前に開始する)が、サーバー上ですべてのリクエストが同時に開始されることは決してありません。

于 2011-09-09T13:37:16.160 に答える
1

GILについて読んだことがありますが、C \ C ++コードを呼び出さずにGILをバイパスする方法はありますか?

あまり。ctypesを介して呼び出された関数は、それらの呼び出しの間、GILを解放します。ブロッキングI/Oを実行する関数もそれを解放します。他にも同様の状況がありますが、それらは常にメインのPythonインタープリターループの外側のコードを含みます。PythonコードでGILを手放すことはできません。

于 2011-09-09T13:09:44.230 に答える
0

また、ソフトウェアの移行メモリを備えたpypyの未来のようなものも見ることができます(したがって、GILは廃止されます)。これは、現時点ではすべて単なる研究と知的嘲笑ですが、何か大きなものに成長する可能性があります。

于 2011-09-09T13:09:33.060 に答える
0

JythonまたはIronPython(および将来的にはPyPy)を使用してコードを実行すると、並行して実行されます

于 2012-10-06T12:31:03.977 に答える