[Python笔记12] asyncio并发编程

1.事件循环

asyncio是python用于解决异步io编程的一整套解决方案
(1)使用asyncio

import asyncio
import time

async def get_html(url):
    print("start get url")
    await asyncio.sleep(2)
    print("end get url")
    
if __name__ == "__main__":
    start_time = time.time()
    loop = asyncio.get_event_loop()
    tasks = [get_html("http://www.baidu.com") for i in range(10)]
    loop.run_until_complete(asyncio.wait(tasks))
    print(time.time() - start_time)

输出结果

start get url
start get url
start get url
start get url
start get url
start get url
start get url
start get url
start get url
start get url
end get url
end get url
end get url
end get url
end get url
end get url
end get url
end get url
end get url
end get url
2.0023303031921387

(2)获取协程的返回值

import asyncio
import time
from functools import partial

async def get_html(url):
    print("start get url")
    await asyncio.sleep(2)
    return "bobby"

def callback(url, future):
    print(url)
    print("send email to bobby")

if __name__ == "__main__":
    start_time = time.time()
    loop = asyncio.get_event_loop()
    task = loop.create_task(get_html("http://www.baidu.com"))
    task.add_done_callback(partial(callback, "http://www.baidu.com"))
    loop.run_until_complete(task)
    print(task.result())

输出结果

start get url
http://www.baidu.com
send email to bobby
bobby

(3)wait 和 gather

import asyncio
import time
async def get_html(url):
    print("start get url")
    await asyncio.sleep(2)
    print("end get url")

if __name__ == "__main__":
    start_time = time.time()
    loop = asyncio.get_event_loop()
    tasks = [get_html("http://www.baidu.com") for i in range(10)]
    loop.run_until_complete(asyncio.gather(*tasks))
    print(time.time()-start_time)

输出结果

start get url
start get url
start get url
start get url
start get url
start get url
start get url
start get url
start get url
start get url
end get url
end get url
end get url
end get url
end get url
end get url
end get url
end get url
end get url
end get url
2.021963357925415
import asyncio
import time
async def get_html(url):
    print("start get url")
    await asyncio.sleep(2)
    print("end get url")

if __name__ == "__main__":
    start_time = time.time()
    loop = asyncio.get_event_loop()
    tasks = [get_html("http://www.baidu.com") for i in range(10)]
    
    group1 = [get_html("http://www.taobao.com") for i in range(2)]
    group2 = [get_html("http://www.jd.com") for i in range(2)]
    group1 = asyncio.gather(*group1)
    group2 = asyncio.gather(*group2)
    loop.run_until_complete(asyncio.gather(group1, group2))
    print(time.time() - start_time)

输出结果

start get url
start get url
start get url
start get url
end get url
end get url
end get url
end get url
2.008810520172119

2.task取消和子协程调用方法

原始代码

import asyncio
import time

async def get_html(sleep_times):
    print("waiting")
    await asyncio.sleep(sleep_times)
    print("done after {}s".format(sleep_times))


if __name__ == "__main__":
    task1 = get_html(2)
    task2 = get_html(3)
    task3 = get_html(3)
    
    tasks = [task1, task2, task3]
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))

输出结果

waiting
waiting
waiting
done after 2s
done after 3s
done after 3s

task取消

import asyncio
import time

async def get_html(sleep_times):
    print("waiting")
    await asyncio.sleep(sleep_times)
    print("done after {}s".format(sleep_times))


if __name__ == "__main__":
    task1 = get_html(2)
    task2 = get_html(3)
    task3 = get_html(3)
    
    tasks = [task1, task2, task3]
    loop = asyncio.get_event_loop()
    
    try:
        loop.run_until_complete(asyncio.wait(tasks))
    except KeyboardInterrupt as e:
        all_tasks = asyncio.Task.all_tasks()
        for task in all_tasks:
            print("cancel task")
            print(task.cancel())
        loop.stop()
        loop.run_forever()
    finally:
        loop.close()

运行后按Ctrl+C取消进程,输出结果

waiting
waiting
waiting
cancel task
True
cancel task
True
cancel task
True
cancel task
True

可以看到任务被取消

3.call方法

(1)call_soon

import asyncio

def callback(sleep_times):
    print("success time {}".format(loop.time()))
    
def stoploop(loop):
    loop.stop()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.call_soon(callback, 2)
    loop.call_soon(stoploop, loop)
    loop.run_forever()

输出结果

success time 34847.328

(2)call_later

import asyncio

def callback(sleep_times):
    print("sleep {} success".format(sleep_times))
    
def stoploop(loop):
    loop.stop()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.call_later(2, callback, 2)
    loop.call_later(1, callback, 1)
    loop.call_later(3, callback, 3)
    loop.run_forever()

输出结果

sleep 1 success
sleep 2 success
sleep 3 success

call_soon 与 call_later比较

import asyncio

def callback(sleep_times):
    print("sleep {} success".format(sleep_times))
    
def stoploop(loop):
    loop.stop()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.call_later(2, callback, 2)
    loop.call_later(1, callback, 1)
    loop.call_later(3, callback, 3)
    loop.call_soon(callback, 4)
    loop.run_forever()

输出结果

sleep 4 success
sleep 1 success
sleep 2 success
sleep 3 success

可以看到call_soon 先于 call_later执行
(3)call_at

import asyncio

def callback(sleep_times):
    print("sleep {} success".format(sleep_times))
def stoploop(loop):
    loop.stop()


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    now = loop.time()
    loop.call_at(now+2, callback, 2)
    loop.call_at(now+1, callback, 1)
    loop.call_at(now+3, callback, 3)
    loop.call_soon(callback, 4)
    loop.run_forever()

输出结果

sleep 4 success
sleep 1 success
sleep 2 success
sleep 3 success

实际时间

**import asyncio

def callback(sleep_times, loop):
    print("success time {}".format(loop.time()))
def stoploop(loop):
    loop.stop()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    now = loop.time()
    loop.call_at(now+2, callback, 2, loop)
    loop.call_at(now+1, callback, 1, loop)
    loop.call_at(now+3, callback, 3, loop)
    loop.call_soon(callback, 4, loop)
    loop.run_forever()**

输出结果

success time 35769.687
success time 35770.687
success time 35771.687
success time 35772.687

4.线程池和asyncio

#使用多线程:在协程中集成阻塞io
import asyncio
from concurrent.futures import ThreadPoolExecutor
import socket
from urllib.parse import urlparse


def get_url(url):
    #通过socket请求html
    url = urlparse(url)
    host = url.netloc
    path = url.path
    if path == "":
        path = "/"

    #建立socket连接
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # client.setblocking(False)
    client.connect((host, 80)) #阻塞不会消耗cpu

    #不停的询问连接是否建立好, 需要while循环不停的去检查状态
    #做计算任务或者再次发起其他的连接请求

    client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(path, host).encode("utf8"))

    data = b""
    while True:
        d = client.recv(1024)
        if d:
            data += d
        else:
            break

    data = data.decode("utf8")
    html_data = data.split("\r\n\r\n")[1]
    print(html_data)
    client.close()


if __name__ == "__main__":
    import time
    start_time = time.time()
    loop = asyncio.get_event_loop()
    executor = ThreadPoolExecutor(3)
    tasks = []
    for url in range(20):
        url = "http://shop.projectsedu.com/goods/{}/".format(url)
        task = loop.run_in_executor(executor, get_url, url)
        tasks.append(task)
    loop.run_until_complete(asyncio.wait(tasks))
    print("last time:{}".format(time.time()-start_time))

输出结果

{"detail":"未找到。"}
{"id":1,"category":{"id":129,"sub_cat":[],"name":"根茎类","code":"gjl","desc":"","category_type":2,"is_tab":false,"add_time":"2017-07-29T18:56:34","parent_category":110},"images":[{"image":"http://shop.projectsedu.com/media/goods/images/1_P_1449024889889.jpg"},{"image":"http://shop.projectsedu.com/media/goods/images/1_P_1449024889264.jpg"},{"image":"http://shop.projectsedu.com/media/goods/images/1_P_1449024889726.jpg"},{"image":"http://shop.projectsedu.com/media/goods/images/1_P_1449024889018.jpg"},{"image":"http://shop.projectsedu.com/media/goods/images/1_P_1449024889287.jpg"}],"goods_sn":"","name":"新鲜水果甜蜜香脆单果约800克","click_num":6435,"sold_num":0,"fav_num":1,"goods_num":-17,"market_price":232.0,"shop_price":156.0,"goods_brief":"食
.....
.....
.....
ages/2_20170719161435_381.jpg\" title=\"\" alt=\"2.jpg\"/></p>","ship_free":true,"goods_front_image":"http://shop.projectsedu.com/media/goods/images/21_P_1448946793276.jpg","is_new":false,"is_hot":false,"add_time":"2017-07-31T23:53:54"}

last time:1.271043300628662

5.asyncio模拟http请求

#asyncio 没有提供http协议的接口 aiohttp
import asyncio
import socket
from urllib.parse import urlparse


async def get_url(url):
    #通过socket请求html
    url = urlparse(url)
    host = url.netloc
    path = url.path
    if path == "":
        path = "/"

    #建立socket连接
    reader, writer = await asyncio.open_connection(host,80)
    writer.write("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(path, host).encode("utf8"))
    all_lines = []
    async for raw_line in reader:
        data = raw_line.decode("utf8")
        all_lines.append(data)
    html = "\n".join(all_lines)
    return html

async def main():
    tasks = []
    for url in range(20):
        url = "http://shop.projectsedu.com/goods/{}/".format(url)
        tasks.append(asyncio.ensure_future(get_url(url)))
    for task in asyncio.as_completed(tasks):
        result = await task
        print(result)

if __name__ == "__main__":
    import time
    start_time = time.time()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    print('last time:{}'.format(time.time()-start_time))

输出结果

HTTP/1.1 404 Not Found
Server: nginx/1.16.1
Date: Wed, 20 May 2020 09:59:48 GMT
Content-Type: application/json
Content-Length: 25
Connection: close
Vary: Accept, Cookie
Allow: GET, HEAD, OPTIONS
X-Frame-Options: SAMEORIGIN


{"detail":"未找到。"}
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Wed, 20 May 2020 09:59:48 GMT
Content-Type: application/json
Content-Length: 980
Connection: close
Vary: Accept, Cookie
Allow: GET, HEAD, OPTIONS
X-Frame-Options: SAMEORIGIN


{"id":6,"category":{"id":130,"sub_cat":[],"name":"茄果类","code":"qgl","desc":"","category_type":2,"is_tab":false,"add_time":"2017-07-29T18:56:34","parent_category":110},"images":[{"image":"http://shop.projectsedu.com/media/goods/images/4_P_1448945381985.jpg"},{"image":"http://shop.projectsedu.com/media/goods/images/4_P_1448945381013.jpg"}],"goods_sn":"","name":"乌拉圭进口牛肉卷特级肥牛卷","click_num":3700,"sold_num":0,"fav_num":0,"goods_num":0,"market_price":90.0,"shop_price":75.0,"goods_brief":"","goods_desc":"<p><img src=\"/media/goods/images/2_20170719161405_249.jpg\" title=\"\" alt=\"2.jpg\"/></p><p><img src=\"/media/goods/images/2_20170719161414_628.jpg\" title=\"\" alt=\"2.jpg\"/></p><p><img src=\"/media/goods/images/2_20170719161435_381.jpg\" title=\"\" alt=\"2.jpg\"/></p>","ship_free":true,"goods_front_image":"http://shop.projectsedu.com/media/goods/images/4_P_1448945381985.jpg","is_new":false,"is_hot":false,"add_time":"2017-07-31T23:53:53"}

....
....
....
last time:0.8495240211486816
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值