2011-08-02 13 views
4

私は自分のプロジェクトをC#から移植していますが、Pythonでマルチスレッドの問題を解決するのに問題があります。問題は長寿命のHTTP要求に関連しており、これは予期されています(要求はサーバーで特定のイベントが発生したときに応答します)。ここに要約があります:HTTPリクエストのクロススレッドを中止する

別のスレッドにurllib2を使用してリクエストを送信します。要求が戻ったりタイムアウトすると、メインスレッドに通知されます。これは正常に動作します。しかし、この未処理のリクエストを中止し、別のURLに切り替える必要がある場合があります。私が検討できるソリューションは4つあります。

  1. 未解決の要求を中止します。 C#にはWebRequest.Abort()がありますが、これをクロススレッドと呼び、リクエストを中止します。 Python urllib2.Requestは純粋なデータクラスであるように見えます。その場合、要求情報のみが格納されます。応答はRequestオブジェクトには接続されません。だから私はこれをすることはできません。
  2. スレッドを中断します。 C#にはThread.Interrupt()があり、待機状態または次回の状態になると、スレッド内でThreadInterruptedExceptionが発生します。 (モニタとファイル/ソケットI/Oの待機はどちらも待機状態です)。Pythonには匹敵するものはありません。 I/Oでブロックされているスレッドを起動する方法はありません。
  3. 要求のタイムアウトを低く設定します。タイムアウトになると、「中止」フラグをチェックします。 falseの場合は、要求を再開します。
  4. オプション3と同様に、状態オブジェクトに「aborted」フラグを追加すると、リクエストが最終的にいずれかの方法で終了すると、スレッドは応答が不要になったことを知り、ただシャットダウンします。

オプション3と4はPythonでサポートされている唯一のものですが、オプション3は恐ろしい解決策であり、4は必要のない接続を維持します。私はもはやそれを必要としないときに良いネチズンとこの接続を閉じることを望んでいます。未解決の要求を実際に中止する方法はありますか?

答えて

2

geventを使用することを検討してください。 Geventは、グリーンレットと呼ばれる非スレッド協調実行単位を使用します。グリーンレットはIO上で「ブロック」することができます。これは実際には「IOが準備ができるまでスリープ状態になる」ことを意味します。ソケットを所有しているリクエスタのグリーンレットと、いつアボートするかを決めるメインのグリーンレットを持つことができます。中止してURLを切り替える場合、主要なグリーンレットはリクエスタのグリーンレットを殺します。リクエスタは結果の例外をキャッチし、socket/urllib2リクエストを閉じてからやり直します。

編集内容:Geventはスレッドと互換性がないため、注意してください。すべての方法でgeventを使用するか、スレッドを途中で使用する必要があります。とにかくGILのため、Pythonのスレッドはちょっとしたことです。

+0

ありがとうございました。私はこれをもう一度書き直す必要があるので、これをバックバーナーに残しておきます。 (現在のところ、パフォーマンス上の理由からスレッドを使用していませんが、コードをシンプルにするため、GILはあまりにも苦労しません) – cdhowie

+0

Geventはpythonのstdlib全体を非同期にパッチすることができます。使いやすい。しかし、確かにあなた自身の意思決定を単純化してください。 –

+0

私は調査しており、これが最善のアプローチだと思われます。ポインタありがとう。 – cdhowie

0

「killable thread」のthis snippetは他に選択肢がない場合に役立ちます。しかし、私はSpike Gronimと同じ意見を持ち、geventを使用することをお勧めします。

1

Spike Gronimの回答に似ていますが、さらに重い手があります。

これをねじれて書き直すことを検討してください。 twisted.web.http.HTTPClientのサブクラス化、特にクライアントの対話を行うためにhandleResponsePartを実装することが望ましいでしょう(応答が終了する前にそれを見る必要がない場合はhandleResponseEnd)。接続を早期に終了するには、クライアントプロトコルでloseConnectionメソッドを呼び出すだけです。

0

私はグーグルを使って、この問題を発見し、思い付くためにスパイクGronimの答えを使用:私はそれが他の人のために役に立つかもしれないと思った

from gevent import monkey 
monkey.patch_all() 
import gevent 
import requests 


def post(*args, **kwargs): 
    if 'stop_event' in kwargs: 
     stop_event = kwargs['stop_event'] 
     del kwargs['stop_event'] 
    else: 
     stop_event = None 

    req = gevent.spawn(requests.post, *args, **kwargs) 

    while req.value is None: 
     req.join(timeout=0.1) 
     if stop_event and stop_event.is_set(): 
      req.kill() 
      break 

    return req.value 

通常のrequest.postと同じように動作しますが、キーワード引数 'stop_event'が必要です。これはthreading.Eventです。 stop_eventが設定されると、要求は中止されます。

接続やコミュニケーションのいずれかを待っていなければ、(前述のように)GILをブロックする可能性があるため、慎重に使用してください。それは(最近のスレッド)と互換性があります(猿のパッチ)。

関連する問題