Pythonコルーチンの詳細なチュートリアル

1.コルーチンの概念

コルーチン:マイクロスレッド、ファイバーとも呼ばれるシングルスレッドでの同時実行。英語名はコルーチンです。

コルーチンとは1つの文で説明されています。コルーチンは、ユーザーモードの軽量スレッドです。つまり、コルーチンはユーザープログラム自体によって制御およびスケジュールされます。

CPUはタスクを実行しており、次の2つの状況で他のタスクを実行するように切り替わります(切り替えはオペレーティングシステムによって強制的に制御されます)。

1つの状況は、タスクがブロックされていることです。もう1つの状況は、タスク
の計算時間が長すぎるか、優先度の高いプログラムがタスクを置き換えていることです。

コルーチンは本質的にスレッドです。以前は、スレッドタスクの切り替えはオペレーティングシステムによって制御されていました。I/ O自動切り替えが発生した場合、コルーチンを使用する目的は、オペレーティングシステムの切り替え(スレッドの切り替え、レジスタの作成、スタックなど、それらを切り替えるなど)、独自のプログラムでタスクの切り替えを制御します。

コルーチンはCpythonインタープリターに指示します、あなたはnbではありませんか?GILロックを使用しませんでしたか?まあ、私はあなたがそれを実行するためのスレッドを作成し、スレッドを切り替える時間を節約します。私はあなたが切り替えるよりも自分で切り替えます。はるかに高速で、多くのオーバーヘッドを回避します。

シングルスレッドの場合、必然的にプログラムにio操作がありますが、独自のプログラムのシングルスレッドで複数のタスクを制御できる場合(つまり、オペレーティングシステムレベルではなくユーザープログラムレベルで)、ioに遭遇する可能性があります。ブロックするときは、別のタスクに切り替えて計算し、スレッドが最大限に準備完了状態、つまり、いつでもcpuによって実行できる状態になるようにします。ユーザープログラムレベルで独自のio操作を最大化するのと同じです。非表示にすると、オペレーティングシステムが混乱し、表示される可能性があります。スレッドは常にコンピューティングを行っているようで、ioが少ないため、より多くのCPU実行権限が割り当てられます。私たちのスレッド。

コルーチンの本質は、単一のスレッドで、ユーザーがタスクを制御し、ioブロッキングが発生したときに実行する別のタスクに切り替えて、効率を向上させることです。それを実現するためには、以下の条件を満たすことができるソリューションを見つける必要があります。

1. 可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续执行。

2. 作为1的补充:可以检测io操作,在遇到io操作的情况下才发生切换

2.コルーチンを実装する方法

1.Greenletがコルーチンを実現☆☆☆

Greenletはコルーチンを実装するための古代の方法です

pip3 install greenlet
from greenlet import greenlet

def eat(name):
    print('%s eat 1' % name)  # 2
    g2.switch('taibai')  # 3
    print('%s eat 2' % name)  # 6
    g2.switch()  # 7

def play(name):
    print('%s play 1' % name)  # 4
    g1.switch()  # 5
    print('%s play 2' % name)  # 8

g1 = greenlet(eat)
g2 = greenlet(play)

g1.switch('taibai')  # 可以在第一次switch时传入参数,以后都不需要  #1

2.yield☆

コルーチンの一種ですが、卵には役立ちません

def func1():
  yield 1
  yield from func2
  yield 2
 def func2():
  yield 3
  yield 4
f1 = func1()
for item in f1:
  print(item)
"""
1
3
4
2
"""

3. Geventモジュール☆☆☆☆(4を直接見ることもできます)

Geventは、geventを介して同時同期または非同期プログラミングを簡単に実装できるサードパーティライブラリです。geventで使用されるメインモードはGreenletです。これは、C拡張モジュールの形式でPythonに接続する軽量コルーチンです。グリーンレットはすべてメインプログラムのオペレーティングシステムプロセス内で実行されますが、協調してスケジュールされます。

i。インストール

pip3 install gevent

ii。使用法

g1=gevent.spawn(func,1,2,3,x=4,y=5)
# 创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的,spawn是异步提交任务

g2=gevent.spawn(func2)

g1.join() #等待g1结束

g2.join() #等待g2结束  有人测试的时候会发现,不写第二个join也能执行g2,是的,协程帮你切换执行了,但是你会发现,如果g2里面的任务执行的时间长,但是不写join的话,就不会执行完等到g2剩下的任务了


#或者上述两步合作一步:
gevent.joinall([g1,g2])

g1.value #拿到func1的返回值

IOブロッキングが発生すると、タスクを自動的に切り替えます

import gevent

def eat(name):
    print('%s eat 1' % name)
    gevent.sleep(2)
    print('%s eat 2' % name)

def play(name):
    print('%s play 1' % name)
    gevent.sleep(1)
    print('%s play 2' % name)


g1 = gevent.spawn(eat, 'egon')
g2 = gevent.spawn(play, name='egon')
g1.join()
g2.join()
# 或者gevent.joinall([g1,g2])
print('主')

上記の例gevent.sleep(2)は、geventが認識できるioブロッキングをシミュレートしています。

およびtime.sleep(2)その他のブロッキングでは、geventを直接識別することはできません。次のコード行とパッチを使用して識別する必要があります。

from gevent import monkey;
monkey.patch_all() #必须放到被打补丁者的前面,如time,socket模块之前

または、単に覚えています。geventを使用するにfrom gevent import monkey;monkey.patch_all()は、ファイルの先頭配置する必要があります。

from gevent import monkey

monkey.patch_all()  # 必须写在最上面,这句话后面的所有阻塞全部能够识别了

import gevent  # 直接导入即可
import time

def eat():
    # print()  
    print('eat food 1')
    time.sleep(2)  # 加上monkey就能够识别到time模块的sleep了
    print('eat food 2')

def play():
    print('play 1')
    time.sleep(1)  # 来回切换,直到一个I/O的时间结束,这里都是我们个gevent做得,不再是控制不了的操作系统了。
    print('play 2')

g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
gevent.joinall([g1, g2])
print('主')

threading.current_thread().getName()各g1とg2を表示するために使用できます。結果を表示するDummyThread-nと、実際には、内部のスレッドに偽のスレッド、仮想スレッドがあります。

プロセススレッドのタスク切り替えは、オペレーティングシステム自体によって切り替えられ、自分で制御することはできません。

コルーチンは、それ自体で制御できる独自のプログラム(コード)を介して切り替えられます。コルーチンモジュールが認識できるIO操作に遭遇した場合にのみ、プログラムはタスクを切り替えて同時効果を実現します。すべてのプログラムにIO操作がない場合、基本的にシリアル実行に属します。

iii。まとめ

これは魔法の武器ですが、剣は古く、オフィシャルにはコルーチンツールが組み込まれています。この方法はasyncioによって排除されようとしています。

4.asyncio☆☆☆☆☆☆☆

pyhon3.4、組み込みモジュール、インストールの必要がないときに導入されました。Pythonインタープリターのバージョンが3.4より大きいことを確認してください

i。コードを直接アップロードします。

import asyncio


@asyncio.coroutine #表示这不再是一个普通函数,已经升级为可以异步的战斗机了!
def func1():
    print(1)
    yield from asyncio.sleep(2)  #模拟io,生产中换成实际的io
    print(2)


@asyncio.coroutine
def func2():
    print(3)
    yield from asyncio.sleep(2)
    print(4)

#把任务放进任务池中
tasks = [
    asyncio.ensure_future(func1()),
    asyncio.ensure_future(func2()),
]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

ii.async&awaitキーワード

Python3.5のみが表示されました。ご使用のバージョンが、要件を満たしていることを確認してください

基本的に上記の使用法と同じですが、キーワードを置き換えるだけです

import asyncio


async def func1():  #async替换掉关键字
    print(1)
    await asyncio.sleep(2)  #等待io
    print(2)


async def func2():
    print(3)
    await asyncio.sleep(2) 
    print(4)


tasks = [
    asyncio.ensure_future(func1()),#future对象较为底层,是task的基类
    asyncio.ensure_future(func2()),
]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

iii.runメソッド、タスクオブジェクト

Python3.7のみが表示されました。バージョンが要件を満たしていることを確認してください

runメソッドには次のものが含まれます

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

最も一般的に使用されるコルーチンメソッド

import asyncio


async def func1():  # async替换掉关键字
    print(1)
    await asyncio.sleep(2)  # 等待io
    print(2)
    return "func1"


async def func2():
    print(3)
    await asyncio.sleep(2)
    print(4)
    return "func2"


async def main():
    tasks = [
        asyncio.create_task(func1()),  # 必须声名在在异步函数中,因为放在外面没有task对象会报错
        asyncio.create_task(func2()),
    ]
    done, pending = await asyncio.wait(tasks, timeout=None)  # done:返回结果,pending:返回未完成的协程函数
    for i in done:
        print(i.result())
    print(pending)


# 运行
asyncio.run(main())

タスクオブジェクトを外部に配置する場合は、コードを変更する必要があります

import asyncio


async def func1():  # async替换掉关键字
    print(1)
    await asyncio.sleep(2)  # 等待io
    print(2)
    return "func1"


async def func2():
    print(3)
    await asyncio.sleep(2)
    print(4)
    return "func2"


tasks = [
    func1(),#不能使用task对象,因为还没有创建事件循环loop对象
    func2(),
]
#wait方法自动把协程函数创建task对象
done, pending = asyncio.run(asyncio.wait(tasks, timeout=None))  # done:返回结果,pending:返回未完成的协程函数
for i in done:
    print(i.result())
print(pending)

iv。リクエストなど、一部のライブラリは非同期構文をサポートしていません

asyncioモジュールを使用して非同期クローラーを実装する場合

import asyncio
import requests

urls = [
    "http://www.smilenow.top",
    "http://www.baidu.com",
    "http://www.163.com"
]


async def get_cont(url):
    print("准备下载:", url)
    htm = requests.get(url=url).text
    print(url, "已经下载完毕")
    return url


tasks = map(lambda x: get_cont(x), urls)

asyncio.run(asyncio.wait(tasks, timeout=None))  # done:返回结果,pending:返回未完成的协程函数

結果

准备下载: http://www.baidu.com
http://www.baidu.com 已经下载完毕
准备下载: http://www.163.com
http://www.163.com 已经下载完毕
准备下载: http://www.smilenow.top
http://www.smilenow.top 已经下载完毕

なんてこった、それはまったく非同期ではないのですか?実行する方法?代わりにスレッドプールを使用してください!

import asyncio
import requests

urls = [
    "http://www.smilenow.top",
    "http://www.baidu.com",
    "http://www.163.com"
]


async def get_cont(url):
    print("准备下载:", url)
    loop = asyncio.get_event_loop()
    future = loop.run_in_executor(None,requests.get,url)#变成多线程方式运行了
    await future
    print(url, "已经下载完毕")
    return url


tasks = map(lambda x: get_cont(x), urls)

asyncio.run(asyncio.wait(tasks, timeout=None))  # done:返回结果,pending:返回未完成的协程函数

v.asyncio非同期操作redis

非同期をサポートするredisモジュールをダウンロードする

pip install aioredis
import aioredis
import asyncio
 
class Redis:
    _redis = None
 
    async def get_redis_pool(self, *args, **kwargs):
        if not self._redis:
            self._redis = await aioredis.create_redis_pool(*args, **kwargs)
        return self._redis
 
    async def close(self):
        if self._redis:
            self._redis.close()
            await self._redis.wait_closed()
 
 
async def get_value(key):
    redis = Redis()
    r = await redis.get_redis_pool(('127.0.0.1', 6379), db=7, encoding='utf-8')
    value = await r.get(key)
    print(f'{key!r}: {value!r}')
    await redis.close()         
 
if __name__ == '__main__':
    asyncio.run(get_value('key'))  # need python3.7

vi.aiomysql非同期操作mysql

インストール:

pip install aiomysql
import asyncio
import aiomysql


async def execute():
    conn = await aiomysql.connect(host='localhost', port=3306, user="root", password='123', db='my')
    cur = await conn.cursor()
    await cur.excute("select * from user")
    result = await cur.fetchall()
    print(result)
    await cur.close()
    await conn.close()


asyncio.run(execute())

v。十分な速さではありません、uvloopはスピードを飛ばします!

uvloopはasyncioを高速化します。実際、nodejs、gevent、およびその他のPython非同期フレームワークの少なくとも2倍の速度です。uvloop asyncioのパフォーマンスベースのテストは、Goプログラムに近いものです。

これは主要なフレームワークが好むモジュールです

インストール:

pip install uvloop
import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

#下面正常书写asyncio代码

おすすめ

転載: blog.csdn.net/qq_40837794/article/details/109708891