day 31协程

Event事件

作用:用来控制线程的执行,由一些线程去控制另一些线程。

from threading import Event
from threading import Thread
import time

#调用Event类实例化一个对象
e=Event()

#若是该方法出现在任务中,则为False,阻塞
# e.wait()#False

#若该方法出现在任务中,则将其他线程的False改为True,进入就绪态与运行态
# e.set()#True

def light():
    print('红灯亮...')
    time.sleep(5)
    #应该开始发送信号,告诉其他线程准备执行
    e.set()#将car中的False变成True
    print('绿灯亮')

def car(name):
    print(f'{name}正在等待红灯。。。')
    #让所有汽车任务进入阻塞态
    e.wait()#False
    print(f'{name}开始飘逸')

#让一个light线程任务,控制多个car线程任务
t=Thread(target=light)
t.start()
for line in range(10):
    t=Thread(target=car,args=(f'童子军坦克{line}号',))
    t.start()
'''红灯亮...
童子军坦克0号正在等待红灯。。。
童子军坦克1号正在等待红灯。。。
童子军坦克2号正在等待红灯。。。
童子军坦克3号正在等待红灯。。。
童子军坦克4号正在等待红灯。。。
童子军坦克5号正在等待红灯。。。
童子军坦克6号正在等待红灯。。。
童子军坦克7号正在等待红灯。。。
童子军坦克8号正在等待红灯。。。
童子军坦克9号正在等待红灯。。。
绿灯亮
童子军坦克4号开始飘逸
童子军坦克6号开始飘逸
童子军坦克7号开始飘逸
童子军坦克0号开始飘逸
童子军坦克3号开始飘逸
童子军坦克5号开始飘逸
童子军坦克2号开始飘逸
童子军坦克8号开始飘逸
童子军坦克1号开始飘逸
童子军坦克9号开始飘逸'''

线程池与进程池

​ 1)什么是进程池与线程池

​ 进程池与线程池是用来控制当前程序允许(进程/线程)的数量。

​ 2)进程池与线程池的作用

​ 保证在硬件允许的范围内创建(进程/线程)的数量

​ 3)如何使用:

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time

ProcessPoolExecutor(5)#5代表只能开启5个进程
ProcessPoolExecutor()#默认以CPU的个数限制进程数

pool=ThreadPoolExecutor(5)#5代表只能开启5个线程
ThreadPoolExecutor()#默认以CPU个数*5限制线程数

#pool.submit('传函数地址')#异步提交任务
# def task():
#     print('线程任务开始了。。。')
#     time.sleep(1)
#     print('线程任务结束了')
#
# for line in range(5):
#     pool.submit(task)
    #等同于t=Tread()  t.start()
'''线程任务开始了。。。
线程任务结束了
线程任务开始了。。。
线程任务结束了
线程任务开始了。。。
线程任务结束了
线程任务开始了。。。
线程任务结束了
线程任务开始了。。。
线程任务结束了'''


#pool.submit('传函数地址').add_done_callback('回调函数地址')
def task():
    print('线程任务开始了。。。。')
    time.sleep(5)
    print('线程任务结束了。。。')
    return 123

#回调函数
def call_back(res):
    print(type(res))
    #注意:赋值操作不要与接受的res同名
    res2=res.result()
    print(res2)

for line in range(5):
    pool.submit(task).add_done_callback(call_back)
'''线程任务开始了。。。。
线程任务开始了。。。。
线程任务开始了。。。。
线程任务开始了。。。。
线程任务开始了。。。。
线程任务结束了。。。
<class 'concurrent.futures._base.Future'>
123
线程任务结束了。。。
<class 'concurrent.futures._base.Future'>
123
线程任务结束了。。。
<class 'concurrent.futures._base.Future'>
123
线程任务结束了。。。
线程任务结束了。。。
<class 'concurrent.futures._base.Future'>
123
<class 'concurrent.futures._base.Future'>
123'''

高性能爬取梨视频

'''
1.从主页中获取所有的视频ID号(1615201,1614813)
    -拼接视频详情url
     https://www.pearvideo.com/video_ + '视频ID号'

2.在视频详情页中获取真实视频Url

3.往真实视频url地址发送请求获取 视频 二进制

4.最后把视频二进制数据保存到本地
'''
from concurrent.futures import ThreadPoolExecutor
import requests
import re
import uuid

pool=ThreadPoolExecutor(200)
# 1.发送请求函数
def get_page(url):
    response=requests.get(url)
    return response

#2.解析主页获取视频ID号
def parse_index(response):
    id_list=re.findall('<a href="video_(.*?)".*?>',response.text,re.S)
    return id_list


#3.解析视频详情页获取真实视频链接
def parse_detail(res):
    response=res.result()
    movie_detail_url=re.findall('srcUrl="(.*?)"',response.text,re.S)[0]
    print(f'往视频链接:{movie_detail_url}发送请求')
    #异步往视频详情页链接发送请求,把结果交给
    pool.submit(get_page,movie_detail_url).add_done_callback(save_movie)
    return movie_detail_url

#4.往真实视频链接发送请求,获取数据并保存到本地
def save_movie(res):
    movie_response=res.result()
    name=str(uuid.uuid4())
    print(f'{name}视频开始保存')
    with open(f'{name}.mp4','wb') as f:
        f.write(movie_response.content)
    print('视频下载完毕')


if __name__ == '__main__':
    index_response=get_page('https://www.pearvideo.com/')
    # print(index_response.text)
    id_list=parse_index(index_response)
    print(id_list)
    for id in id_list:
        detail_url='https://www.pearvideo.com/video_'+id
        pool.submit(get_page,detail_url).add_done_callback(parse_detail)

协程

-进程:资源单位

-线程:执行单位

-协程:在单线程下实现并发

注意:协程不是操作系统资源,他是程序起的名字,为让单线程能实现并发

协程的目的:

​ -操作系统:

​ 多道技术,切换+保存状态

​ 1)遇到IO

​ 2)CPU执行时间过长

​ -协程:

​ 通过手动模拟操作系统 “多道技术”,实现 切换+保存状态

​ 1)手动实现 遇到IO切换,七篇操作系统误以为没有IO操作

​ -单线程 遇到IO,切换+保存状态

​ -单线程 计算密集型,来回切换+保存状态 反而效率更低

​ 优点:在IO密集型的情况下,会提高效率

​ 缺点:若在计算密集型的情况下,来回切换+保存状态

​ -yield:保存状态

​ -并发:切换

'''
gevent:
    是一个第三方模块,可以帮你监听IO操作,并切换
使用gevent目的:
    为了实现单线程下,实现遇到IO,保存状态+切换
'''
import time
from gevent import spawn,joinall,monkey#用于切换+保存数据
monkey.patch_all()#检测IO
def func1():
    print('1')
    time.sleep(1)

def func2():
    print('2')
    time.sleep(3)

def func3():
    print('3')
    time.sleep(5)
 

s1=spawn(func1)
s2=spawn(func2)
s3=spawn(func3)
# s1.join()
# s2.join()
# s3.join()
joinall([s3,s2,s1])
'''1
2
3
'''

单协程实现TCP服务端并发

# 服务端
from gevent import monkey,spawn
monkey.patch_all()#检测IO
import socket

server=socket.socket()
server.bind(('127.0.0.1',9527))
server.listen(5)
print('启动服务端。。。')

# 线程任务,执行接受客户端消息与发送端消息给客户
def working(conn):
    while True:
        try:
            print('协程')
            data=conn.recv(1024).decode('utf-8')
            if len(data)==0:
                break
            # print(data)
            send_data=data.upper().encode('utf-8')
            conn.send(send_data)
        except Exception as e:
            print(e)
            break
    conn.close()
#
# def server2():
#     while True:
#         conn,addr=server.accept()
#         spawn(working,conn)
#         # working(conn)
# if __name__ == '__main__':
#     s1=spawn(server2)
#     s1.join()
while True:
    conn, addr = server.accept()
    s1=spawn(working, conn)
    s1.join()

# def server2():
#     while True:
#         conn,addr=server.accept()
#         spawn(working,conn)
#         # working(conn)
# if __name__ == '__main__':
#     # s1=spawn(server2)
#     # s1.join()
#     server2()
#客户端
import socket
from threading import Thread,current_thread

def client():
    client=socket.socket()
    client.connect(('127.0.0.1',9527))
    print('启动客户端。。。')
    number=0
    while True:
        send_data=f'{current_thread().name}{number}'
        client.send(send_data.encode('utf-8'))
        data=client.recv(1024)
        print(data.decode('utf-8'))
        number+=1

for i in range(3):
    t=Thread(target=client)
    t.start()

猜你喜欢

转载自www.cnblogs.com/zqfzqf/p/11735600.html