クローラー 15 - コルーチン非同期

1. コルーチンと非同期の概念

1.1 まずコードをインポートします

import time
def func():
    print("我爱黎明!")
    time.sleep(3) #睡三秒,让当前线程处于堵塞状态。
    print("我真的爱黎明")
if __name__ == '__main__':
    a = time.time()
    func()
    b = time.time()
    print(b - a)

実行結果から判断すると、プログラムが完了するまでに少なくとも 3 秒かかります。

我爱黎明!
我真的爱黎明
3.0042407512664795

1.2 3 秒を超えると、実際には 3 秒間何も作業が行われず、プログラムはスリープ状態に入ります プログラムのスリープ中、プログラムの CPU は動作しません。

例: 人生において、あなたは足を洗いに行きます。足を浸している途中では、技術者はあなたをマッサージすることはできません。あなたは先に他のことをすることができます。足を浸し終えたら、彼らは来ます。あなたをマッサージするために。

1.3 プログラムの作成プロセスを考えると、プログラムをブロック状態にできるステートメントは他にありますか?

# 1. input()语句
# 输入语句 他执行的时候,会让程序停留在终端,等待用户输入,什么时候输入完,再继续往下走

# 2.requests.get(url)
# 当我对url发送请求之后,在网络进行传输,并且url还要准备数据,再传送回来,这个过程我们需要等待服务器那边给我们传输数据,所以也是堵塞状态

#所以总结:
# 一般情况,当程序处于I/O操作的时候(输入,输出),线程会处于堵塞状态

1.4 プログラムがブロックされた状態では、この時点でコルーチン操作が必要になります。

协程:当程序遇见了I/O操作的时候,可以选择性的切换到其他任务上,充分的利用CPU。
在微观上是一个任务一个任务的进行切换,切换条件一般都是IO操作
在宏观上,我们能看到的其实是多个任务一起在执行
# 简称 多任务异步操作

前の例によると、足を浸している間に技術者がマッサージできない場合は、足を浸している間、技術者に肩を揉んでもらい、浸るのが終わるまで待ってからマッサージを続けることができます。

上記はすべてシングルスレッドの条件下です 

2. マルチタスク非同期コルーチン

2.1 まず、非同期コルーチン操作を簡単に理解します

import asyncio # 导入协程模块

async def func():
    print("我真的爱黎明")
if __name__ == '__main__':
    g = func() # 此时的函数是异步协程函数,
    # print(g) # 此时函数执行得到的是一个协程对象
    asyncio.run(g) # 协程程序运行需要asyncio模块的支持

タスクが 1 つだけの場合は、以前に書いたものとあまり変わりません

2.2 今回はマルチタスク非同期コルーチンを導入

まず同期操作を導入します

import time
def func():
    print("你好啊,我叫赛利亚")
    time.sleep(3) # 增加耗时操作
    print("你好啊,我叫赛利亚")
def func1():
    print("你好啊,我叫张东墙")
    time.sleep(2)
    print("你好啊,我叫张东墙")
def func2():
    print("你好啊,我叫王伟")
    time.sleep(4)
    print("你好啊,我叫王伟")

if __name__ == '__main__':
    a=time.time()
    func()
    func1()
    func2()
    b = time.time()
    print(b-a,'s')

同期の場合、プログラムは 1 つずつ実行されますが、実行結果を観察すると、プログラムは 9 秒以上実行されており、そのうち 9 秒はプログラムの合計スリープ時間です。

/Users/wangminjie/Desktop/爬虫/venv/bin/python /Users/wangminjie/Desktop/爬虫/线程进程/协程.py
你好啊,我叫赛利亚
你好啊,我叫赛利亚
你好啊,我叫张东墙
你好啊,我叫张东墙
你好啊,我叫王伟
你好啊,我叫王伟
9.008739948272705 s

2.3 マルチタスク非同期コルーチンの書き方では以下のようになります

import asyncio
import time
async def func():
    print("你好啊,我叫赛利亚")
    time.sleep(3) # 增加耗时操作
    print("你好啊,我叫赛利亚")
async def func1():
    print("你好啊,我叫张东墙")
    time.sleep(2)
    print("你好啊,我叫张东墙")
async def func2():
    print("你好啊,我叫王伟")
    time.sleep(4)
    print("你好啊,我叫王伟")

if __name__ == '__main__':
    a = time.time()
    p1 = func()
    p2 = func1()
    p3 = func2()
    tasks = [p1, p2, p3]
    # 一次性启动多个任务(协程)
    #多个任务的话,就得交给asyncio.wait来处理
    asyncio.run(asyncio.wait(tasks))
    b = time.time()
    print(b - a, 's')

実行結果から、このときの非同期操作の実行時間は上記の同期操作の実行時間と同様であることがわかります。

2.4 理由は time.sleep() が同期動作だからです プログラムに同期動作があると非同期動作が中断されてしまいます この時プログラムのスリープ動作を非同期動作に書き換える必要があります asyncio.sleep 、 await Hangs を使用すると、ハングするとプログラムは次のステップの動作に切り替わります。

import asyncio
import time
async def func():
    print("你好啊,我叫赛利亚")
    # 当程序出现了同步操作的时候,异步就中断了
    #time.sleep(3) # 增加耗时操作
    # await 挂起
    await asyncio.sleep(3) # 异步操作的代码
    print("你好啊,我叫赛利亚")
async def func1():
    print("你好啊,我叫张东墙")
    #time.sleep(2)
    await asyncio.sleep(2)
    print("你好啊,我叫张东墙")
async def func2():
    print("你好啊,我叫王伟")
    #time.sleep(4)
    await asyncio.sleep(4)
    print("你好啊,我叫王伟")

if __name__ == '__main__':
    a = time.time()
    p1 = func()
    p2 = func1()
    p3 = func2()
    tasks = [p1, p2, p3]
    # 一次性启动多个任务(协程)
    #多个任务的话,就得交给asyncio.wait来处理
    asyncio.run(asyncio.wait(tasks))
    b = time.time()
    print(b - a, 's')

このようにしてマルチタスクの非同期コルーチン動作が実現され、全体の所要時間はプログラム内の最長時間にマルチタスクの切り替え時間を加えたものとなります。 

/Users/wangminjie/Desktop/爬虫/venv/bin/python /Users/wangminjie/Desktop/爬虫/线程进程/协程.py
你好啊,我叫王伟
你好啊,我叫赛利亚
你好啊,我叫张东墙
你好啊,我叫张东墙
你好啊,我叫赛利亚
你好啊,我叫王伟
4.005831956863403 s

2.5 ただし、このようにマルチタスクコルーチンを書くとmain関数がほとんど動作してしまうため、書き方を変えて関数をカプセル化して利用し、よりクローラに最適なこのように書く必要があります。

import asyncio
import time
async def func():
    print("你好啊,我叫赛利亚")
    await asyncio.sleep(3) # 异步操作的代码
    print("你好啊,我叫赛利亚")
async def func1():
    print("你好啊,我叫张东墙")
    await asyncio.sleep(2)
    print("你好啊,我叫张东墙")
async def func2():
    print("你好啊,我叫王伟")
    await asyncio.sleep(4)
    print("你好啊,我叫王伟")
async def main():
    # 第一种写法
    # f1 = func()
    # await f1 # 一般await挂起操作放在协程对象前面
    # ....

    # 第二种写法(推荐)
    tasks = [func(), func1(), func2()]
    await asyncio.wait(tasks)
if __name__ == '__main__':
    a = time.time()
    asyncio.run(main())
    b = time.time()
    print(b - a, 's')

【運用結果】

実行効率も以前のプログラムと同じです。

你好啊,我叫张东墙
你好啊,我叫赛利亚
你好啊,我叫王伟
你好啊,我叫张东墙
你好啊,我叫赛利亚
你好啊,我叫王伟
4.006398916244507 s

ただし、出力時に警告が表示されますが、プログラムの実行には影響しません。

asyncio.wait を使用する前に、コルーチン オブジェクトを Tesk オブジェクトにラップするように変更できます。必要なのは、コルーチン オブジェクトの前に asyncio.create_task を追加することだけです。これは、コルーチン タスクを作成するのと同じです。 

#tasks = [func(), func1(), func2()]
    
    tasks = [
        asyncio.create_task(func()),
        asyncio.create_task(func1()),
        asyncio.create_task(func2())
    ]
    
    await asyncio.wait(tasks)

おすすめ

転載: blog.csdn.net/m0_48936146/article/details/124820355