Python連携(geventモジュール)

5736 ワード

一:はじめに
コヒーレンスはマイクロスレッド、ファイバスレッドとも呼ばれます.英語名Coroutine:コパスはユーザー状態の軽量レベルスレッドです
協程は独自のレジスタコンテキストとスタックを持っている.コンシステントスケジューリング切り替え時には、レジスタコンテキストとスタックを別の場所に保存し、切り取ったときに、前に保存したレジスタコンテキストとスタックを復元します.したがって、次のようになります.
コンシステントは、前回呼び出し時の状態を保持し、前回離れたときの論理ストリームの位置に入ることができる
協力のメリット
  • スレッドコンテキスト切替を必要としないオーバーヘッド
  • 原子操作(スレッドスケジューリング機構によって中断されない操作)のロックおよび同期のオーバーヘッド
  • を必要としない.
  • 制御フローの切り替えを容易にし、プログラミングモデル
  • を簡略化する.
  • 高同時+高拡張+低成文:1つのCPUが上完の協程をサポートするのは問題ないので、高同時処理
  • に適しています.
    きょうていの欠点
  • マルチコアリソースを利用できない:コプロセッサの本質は単一スレッドであり、マルチCPU上で
  • を実行するにはプロセスと協力する必要がある.
  • がブロック(Blocking)操作(IOの場合など)を行うと、プログラム
  • 全体がブロックされる.
    きょうていのじょうけん
  • は、1つの単一スレッドのみで同時
  • を実現する必要があります.
  • 共有データの変更
  • ロック不要
  • ユーザプログラムに複数の制御フローを自己保存するコンテキストスタック
  • コパスがIO操作に遭遇すると、自動的に他のコパス
  • に切り替える.
    yieldを使用したコラボレーション
    def consumer(name):
        print("--->starting eating baozi...")
        while True:
            new_baozi = yield
            print("[%s] is eating baozi %s" % (name, new_baozi))
    
    def producer():
        next(con)
        next(con2)
        n = 0
        while n < 5:
            n += 1
            con.send(n)
            con2.send(n)
            print("\033[32;1m[producer]\033[0m is making baozi %s" % n)
    
    if __name__ == '__main__':
        con = consumer("c1")
        con2 = consumer("c2")
        p = producer()

    二:Greenlet
    グリーンレットはCで実装されたコラボレーションモジュールで、pythonが持参したyieldに比べて、generatorとして宣言する必要がなく、任意の関数の間で自由に切り替えることができます.
    グリーンレットを使用したコラボレーション
    from greenlet import greenlet
    def f1():
        print(12)
        gr2.switch()
        print(34)
        gr2.switch()
    def f2():
        print(56)
        gr1.switch()
        print(78)
    if __name__=='__main__':
        gr1 = greenlet(f1)
        gr2 = greenlet(f2)
        gr1.switch()     #    ,gevent  greenlet   ,      
    
    #     12
    56
    34
    78

    三:Gevent
    Geventはサードパーティ製のライブラリ(追加で自分でインストールする必要がある)であり、geventによって容易に同時同期または非同期プログラミングを実現することができ、geventで主に使用されるモードはGreenletであり、C拡張モジュールの形式でPythonにアクセスする軽量レベルのコラボレーションである.Greenletはすべてメインプログラムオペレーティングシステムの内部で実行され、コラボレーションスケジューリングされる
    geventライブラリを使用したコラボレーション
    import gevent
    
    def func1():
        print("func1 running")
        gevent.sleep(2)             #       io  
        print("switch func1")
    
    def func2():
        print("func2 running")
        gevent.sleep(1)
        print("switch func2")
    
    def func3():
        print("func3  running")
        gevent.sleep(0.5)
        print("func3 done..")
    
    if __name__=='__main__':
        gevent.joinall([gevent.spawn(func1),
                        gevent.spawn(func2),
                        gevent.spawn(func3),
                        ])

    同期と非同期のパフォーマンスの違い
    import gevent
    
    def task(pid):
        """
        Some non-deterministic task
        """
        gevent.sleep(0.5)
        print('Task %s done' % pid)
    
    def synchronous():
        for i in range(1, 10):
            task(i)
    
    def asynchronous():
        threads = [gevent.spawn(task, i) for i in range(10)]
        gevent.joinall(threads)
    
    if __name__ =='__main__':
        print('Synchronous:')
        synchronous()
    
        print('Asynchronous:')
        asynchronous()

    task関数はGreenlet内部スレッドのgevent.spawnにカプセル化される.初期化されたgreenletリストは、現在のプロセスをブロックし、所与のgreenletをすべて実行するthreads関数に渡される配列gevent.joinallに格納される.実行プロセスは、すべてのgreenletが実行された後にのみ下に進みます.  
    IOブロックに遭遇すると自動的にタスクが切り替わります
    from gevent import monkey
    
    monkey.patch_all()
    import gevent
    from  urllib.request import urlopen
    
    def f(url):
        print('GET: %s' % url)
        resp = urlopen(url)
        data = resp.read()
        print('%d bytes received from %s.' % (len(data), url))
    
    if __name__=='__main__':
        gevent.joinall([
            gevent.spawn(f, 'https://www.python.org/'),
            gevent.spawn(f, 'https://www.yahoo.com/'),
            gevent.spawn(f, 'https://github.com/'),
        ])

    geventによる単一スレッドでのマルチソケット同時実行
    serverエンド
    import sys
    import socket
    import time
    import gevent
     
    from gevent import socket,monkey
    monkey.patch_all()
     
     
    def server(port):
        s = socket.socket()
        s.bind(('0.0.0.0', port))
        s.listen(500)
        while True:
            cli, addr = s.accept()
            gevent.spawn(handle_request, cli)
     
     
     
    def handle_request(conn):
        try:
            while True:
                data = conn.recv(1024)
                print("recv:", data)
                conn.send(data)
                if not data:
                    conn.shutdown(socket.SHUT_WR)
     
        except Exception as  ex:
            print(ex)
        finally:
            conn.close()
    if __name__ == '__main__':
        server(8001)

    クライアント側
    import socket
     
    HOST = 'localhost'    # The remote host
    PORT = 8001           # The same port as used by the server
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    while True:
        msg = bytes(input(">>:"),encoding="utf8")
        s.sendall(msg)
        data = s.recv(1024)
        #print(data)
     
        print('Received', repr(data))
    s.close()

    同時100ソケット接続
    import socket
    import threading
    
    def sock_conn():
    
        client = socket.socket()
    
        client.connect(("localhost",8001))
        count = 0
        while True:
            #msg = input(">>:").strip()
            #if len(msg) == 0:continue
            client.send( ("hello %s" %count).encode("utf-8"))
    
            data = client.recv(1024)
    
            print("[%s]recv from server:" % threading.get_ident(),data.decode()) #  
            count +=1
        client.close()
    
    
    for i in range(100):
        t = threading.Thread(target=sock_conn)
        t.start()