ホームページ  >  記事  >  バックエンド開発  >  Python がフューチャーを通じて同時実行の問題を処理する方法の詳細な例

Python がフューチャーを通じて同時実行の問題を処理する方法の詳細な例

黄舟
黄舟オリジナル
2018-05-11 17:54:422035ブラウズ

この記事では主に、future を通じて同時実行の問題を処理するための Python を紹介します。これは非常に優れており、必要な友人は参考にしてください。 futureの予備理解:

例1: 通常のループメソッド

import os
import time
import sys
import requests
POP20_CC = (
 "CN IN US ID BR PK NG BD RU JP MX PH VN ET EG DE IR TR CD FR"
).split()
BASE_URL = 'http://flupy.org/data/flags'
DEST_DIR = 'downloads/'
def save_flag(img,filename):
 path = os.path.join(DEST_DIR,filename)
 with open(path,'wb') as fp:
 fp.write(img)
def get_flag(cc):
 url = "{}/{cc}/{cc}.gif".format(BASE_URL,cc=cc.lower())
 resp = requests.get(url)
 return resp.content
def show(text):
 print(text,end=" ")
 sys.stdout.flush()
def download_many(cc_list):
 for cc in sorted(cc_list):
 image = get_flag(cc)
 show(cc)
 save_flag(image,cc.lower()+".gif")
 return len(cc_list)
def main(download_many):
 t0 = time.time()
 count = download_many(POP20_CC)
 elapsed = time.time()-t0
 msg = "\n{} flags downloaded in {:.2f}s"
 print(msg.format(count,elapsed))
if __name__ == '__main__':
 main(download_many)

例2: futureメソッドで実装、ここでは上記のコードの一部を再利用します

 from concurrent import futures
from flags import save_flag, get_flag, show, main
MAX_WORKERS = 20
def download_one(cc):
 image = get_flag(cc)
 show(cc)
 save_flag(image, cc.lower()+".gif")
 return cc
def download_many(cc_list):
 workers = min(MAX_WORKERS,len(cc_list))
 with futures.ThreadPoolExecutor(workers) as executor:
 res = executor.map(download_one, sorted(cc_list))
 return len(list(res))
if __name__ == '__main__':
 main(download_many)

それぞれ3回実行し、両方の平均速度: 13.67 秒と 1.59 秒、その差は依然として非常に大きいことがわかります。

future

future は、concurrent.futures モジュールと asyncio モジュールの重要なコンポーネントです

python3.4 以降、標準ライブラリには Future という名前のクラスが 2 つあります: concurrent.futures.Future Futureこれら 2 つのクラスは同じ目的を果たします。両方の Future クラスのインスタンスは、完了する場合と完了しない場合がある遅延計算を表します。 Twisted の Deferred クラスと Tornado フレームワークの Future クラスに似ています 注: 通常、Future は自分で作成するべきではなく、並行フレームワーク (concurrent.futures または asyncio) によってインスタンス化されるべきです
理由: Future は終わりを表します。何かが起こるかどうかを判断する唯一の方法は、実行時間がスケジュールされているため、 concurrent.futures.Future インスタンスは、何かが concurrent.futures.Executor サブクラスに渡されたときにのみ作成されます。

例: Executor.submit() メソッドのパラメータは呼び出し可能オブジェクトです。このメソッドを呼び出した後、時刻は着信呼び出し可能オブジェクトに対してスケジュールされ、


future

クライアント コードは将来の状態を変更できません。同時実行フレームワークは、future によって表される遅延計算が終了した後に future オブジェクトの状態を変更します。計算がいつ終了するかを制御することはできません。

どちらのフューチャーにも .done() メソッドがあります。このメソッドはブロックされず、戻り値はブール値で、フューチャーにリンクされた呼び出し可能オブジェクトが実行されたかどうかを示します。クライアント コードは通常、Future の実行が終了したかどうかを尋ねず、通​​知を待ちます。したがって、両方の Future クラスには .add_done_callback() メソッドがあり、このメソッドにはパラメーターが 1 つだけあり、その型は呼び出し可能オブジェクトであり、Future の実行後に呼び出されます。

.result() メソッドには、2 つの Future クラスで同じ機能があります。呼び出し可能なオブジェクトの結果を返すか、呼び出し可能なオブジェクトの実行時にスローされた例外を再スローします。ただし、Future の実行が終了しない場合、2 つの Future クラスの result メソッドの動作は大きく異なります。


concurrent.futures.Future インスタンスの場合、 .result() メソッドを呼び出すと、返される結果が存在するまで呼び出し元のスレッドがブロックされます。この時点で、result メソッドはオプションのタイムアウト パラメーターを受け取ることができます。 Future が時間内に実行を終了しない場合、TimeoutError 例外がスローされます。

asyncio.Future.result メソッドはタイムアウトの設定をサポートしていません。将来の結果を取得するには、構造体からのyieldを使用するのが最善ですが、concurrent.futures.Future ではこれができません

asyncio であっても concurrent.futures であっても。 .Future では、いくつかの関数が future を返し、他の関数は future を使用します。最初の例では、戻り値はイテレータの __next__ メソッドで呼び出されます。 Future.as_completed 関数の使用に関して、ここでは 2 つのループを使用します。1 つは将来の作成とスケジュールに使用され、もう 1 つは結果

from concurrent import futures
from flags import save_flag, get_flag, show, main
MAX_WORKERS = 20
def download_one(cc):
 image = get_flag(cc)
 show(cc)
 save_flag(image, cc.lower()+".gif")
 return cc
def download_many(cc_list):
 cc_list = cc_list[:5]
 with futures.ThreadPoolExecutor(max_workers=3) as executor:
 to_do = []
 for cc in sorted(cc_list):
  future = executor.submit(download_one,cc)
  to_do.append(future)
  msg = "Secheduled for {}:{}"
  print(msg.format(cc,future))
 results = []
 for future in futures.as_completed(to_do):
  res = future.result()
  msg = "{}result:{!r}"
  print(msg.format(future,res))
  results.append(res)
 return len(results)
if __name__ == '__main__':
 main(download_many)

結果は次のとおりです:


注: ブロッキング IO 操作を実行する標準ライブラリ内のすべての関数は、オペレーティング システムの待機中に GIL を解放します。実行のために他のスレッドを実行します。これがあるからこそ、Python スレッドは IO 集中型のアプリケーションで役割を果たすことができます

上記はスレッドを開始する concurrent.futures であり、次はプロセスを開始するものです。それを通して

concurrent.futuresを使用してプロセスを開始します

concurrent.futures ProcessPoolExecutorクラスは作業を複数のPythonプロセスに分散するため、CPUを集中的に使用する処理を行う必要がある場合は、このモジュールを使用してGILをバイパスし、すべてのCPUコア。

その原理は、ProcessPoolExecutor が N 個の独立した Python インタープリターを作成することです。ここで、N はシステムで利用可能な CPU コアの数です。

使用方法はThreadPoolExecutorメソッドと同じです

以上がPython がフューチャーを通じて同時実行の問題を処理する方法の詳細な例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。