asyncico多合併タスクはtoo many file descriptors in selectの解決構想を誤報します。

3856 ワード

問題の説明:
s 3プログラムはtonado web人顔検出機能をテストするテスト関数です。コンカレントにいますreaquestの写真の数が大きいとエラーが発生します。
async def request_images(images):
    async with aiohttp.ClientSession(json_serialize=ujson.dumps) as session:
        url = "http://localhost:9999/images"
        payload = {'instances': images}

        async with session.post(url, json=payload) as resp:
            v= await resp.text()
                        print(v)

async def main():
...
  concurrent_requests = (request_images(re_im2) for i in range(10000))
  await asyncio.gather(*concurrent_requests)
...
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
エラーメッセージ:
Traceback (most recent call last):
  File "C:/Users/14192/MTCNN-zzk/MTCNN/s3aiosend_test.py", line 58, in 
    loop.run_until_complete(main())
  File "C:\Users\14192\Anaconda3\lib\asyncio\base_events.py", line 454, in run_until_complete
    self.run_forever()
  File "C:\Users\14192\Anaconda3\lib\asyncio\base_events.py", line 421, in run_forever
    self._run_once()
  File "C:\Users\14192\Anaconda3\lib\asyncio\base_events.py", line 1389, in _run_once
    event_list = self._selector.select(timeout)
  File "C:\Users\14192\Anaconda3\lib\selectors.py", line 323, in select
    r, w, _ = self._select(self._readers, self._writers, [], timeout)
  File "C:\Users\14192\Anaconda3\lib\selectors.py", line 314, in _select
    r, w, x = select.select(r, w, w, timeout)
ValueError: too many file descriptors in select()
コード解析
コードから見て、非同期方法mainでは、concurrent_requestsはリストジェネレータで、中の各オブジェクトは一回tonadoのサービスコールで結果を返します。リスト生成器は関数asyncico.gatherによって呼び出され、Awaitableオブジェクトを返します。ここのawaitキーワードは配合関数asyncのキーワードで使われています。自分の協働を保留して、もう一つの協働が終わるまで待つことを示しています。
main関数は協働オブジェクトとして定義されていますが、その呼び出しはすぐに関数を実行することなく、協働オブジェクトに戻ります。協働対象はイベントサイクルに登録する必要があり、イベントサイクルから呼び出します。したがって、コードの最後にloop = asyncio.get_event_loop() loop.run_until_complete(main())という2行のコードを使用して、イベントサイクルを開始し、イベントサイクルにmain関数という協働を登録し、イベントサイクルを開始する。
エラーメッセージもイベントサイクルからきています。until_completteの方法
イベントループループに登録されているasync main方法だけが協働taskとして処理されると見られます。コードの中ではmainメソッドを全体的に協働と見なしています。この協働範囲内のコードにはawaitが現れています。つまり、この関数が詰まると、この協働は他の協働を呼び出すために制御権を委譲します。私たちが実現したいのは、main関数の内部で、複数のtonado webクエリに対して、複数のtonadoクエリを異なるプロセスと見なしてもいいです。メールを协途とするのではなく、mainの中でawaitに会ってmainプログラムを挂けます。ここには协调しかないので、これは主要な问题ではなく、美化する部分だけが必要です。
コードの主な問題はasyncico内部でselect方法を使用しています。開くファイルの文字長に対して、select方法は最大の制限があります。一度に多すぎるweb呼び出し関数をタスクとして必要とすると、Listが大きすぎてエラーが発生します。同時進行の思想は間違っていません。ここでコールバックインターフェースを使ったほうがいいです。
考えをまとめる
私たちが望んでいるのは、tonadoに対するサービスの呼び出しが並行して行われており、コールバックインターフェースを使って合併を実現するためのtonadoサービスへのアクセスです。
そこで、まずtonadoへのアクセス関数をサポートコール方式に書きます。
async def request_images(images):
    async with aiohttp.ClientSession(json_serialize=ujson.dumps) as session:
        url = "http://localhost:9999/images"
        payload = {'instances': images}

        async with session.post(url, json=payload) as resp:
            return await resp.text()

def post_deal(content):
    print(content)
loopイベントをmain関数にループし、訪問ごとにtonadoサービスへの非同期呼び出しを実現します。
def main():
    face_img = cv2.imread(im_name)
    re_im = face_img
    re_im2 = load_image(im_name)
    import time
    start = time.time()
    loop = asyncio.get_event_loop()
    # concurrent_requests = [asyncio.ensure_future(request_images(re_im2)) for i in range(10000)]
    for i in range(10000):
        concurrent_request = asyncio.ensure_future(request_images(re_im2))
        concurrent_request.add_done_callback(post_deal)
        loop.run_until_complete(concurrent_request)
    end = time.time() - start
    print(end)

if __name__ == '__main__':
    main()
このようにコードの問題は非同期フィードバックによって解決されました。
参考文章pythonの重要モジュール-asynic asyncic非同期爬虫類